在实际项目中,往往需要对各种接口的参数进行检验,比如不能为空,必须处于某个范围等等,传统的检验方式就是自定义代码去检验参数,代码量大,繁琐冗余。于是有第三方发布了参数检验的插件,本文介绍2种(javax.validation 和 Preconditions)。
心得:先对参数进行检验,如果有问题再抛出异常什么的,如果没有问题,那就执行逻辑部分,如果执行有问题,那就抛出异常,并考虑做出怎样的补救措施。
javax.validation 和 Preconditions 方式各有所长,两者配合使用会更好,更方便。
1、javax.validation方式
添加maven依赖,其中javax.validation.validation-api是定义了接口,具体的实现是由hibernate-validator完成的。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.2.Final</version>
</dependency>
javaz.validation 给出了很多注解,用于规定哪些参数必须要满足什么样的要求,否则就会抛出异常。这些注解一般用于实体类的成员变量上。
注解 | 功能描述 |
@Null | 被注解的元素必须为null |
@NotNull | 被注解的元素必须不为null |
@AssertTrue | 被注解的元素必须为true |
@AssertFalse | 被注解的元素必须为false |
@Min(value) | 被注解的元素必须为数字,其值必须大于等于最小值 |
@Max(value) | 被注解的元素必须为数字,其值必须小于等于最小值 |
@Size(max,min) | 被注解的元素的大小必须在指定范围内 |
@Past | 被注解的元素必须为过去的一个时间 |
@Future | 被注解的元素必须为未来的一个时间 |
@Pattern | 被注解的元素必须符合指定的正则表达式 |
@Digits(integer, fraction) | 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。 |
@DecimalMax | 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度 |
@DecimalMin | 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度 |
@Email | 验证是否是邮件地址,如果为null,不进行验证,算通过验证。 |
@NotBlank(massege) | 被标注的字符串必须是非 null,否则抛出异常,异常信息为 message |
@Range(Min, Max, message) | 被标注的元素必须处于范围内,否则抛出异常,异常信息为 message |
@Length(Min, Max, message) | 被标注的字符串的长度必须在范围内,否则抛出异常,异常信息为 message |
@NotEmpty | |
NotNull、NotBlank 和 NotEmpty 的区别,引自文章:
@NotNull
适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty)
@NotBlank
适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0
@NotEmpty
适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0
一般在开发中,Javax.validation 注解用于在实体类的成员变量上,然后在 controller 层的类方法的参数前添加上 @Valid,即可,这样才能触发对参数的约束。
2、Preconditions方式
添加maven依赖。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
Preconditions类提供了一些参数检验的静态方法(主要的),针对参数的错误类型,会有不同的异常类型,开发者可以针对异常类型去做一些警告、日志、反馈措施等等。
Preconditions 类的作用更适合检查单一的参数。
@GwtCompatible
public final class Preconditions {
private Preconditions() {}
//检查表达式是否为真,否则抛出异常
public static void checkArgument(boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
//检查表达式是否为真,否则抛出异常(带异常的描述)
public static void checkArgument(boolean expression, @Nullable Object errorMessage) {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
//检查表达式是否为真,否则抛出异常(带异常的描述,描述中可参入变量内容)
public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(Strings.lenientFormat(errorMessageTemplate, errorMessageArgs));
}
}
//同上,只是抛出的异常类型不同
public static void checkState(boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
}
//类似
public static void checkState(boolean expression, @Nullable Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
//类似
public static void checkState(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
if (!expression) {
throw new IllegalStateException(Strings.lenientFormat(errorMessageTemplate, errorMessageArgs));
}
}
//检查是否为NULL,如果为NULL,则抛出异常
@CanIgnoreReturnValue
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
} else {
return reference;
}
}
//检查是否为NULL,如果为NULL,则抛出异常(带异常的描述)
@CanIgnoreReturnValue
public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
} else {
return reference;
}
}
//与上类似,可参入变量内容
@CanIgnoreReturnValue
public static <T> T checkNotNull(T reference, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
if (reference == null) {
throw new NullPointerException(Strings.lenientFormat(errorMessageTemplate, errorMessageArgs));
} else {
return reference;
}
}
@CanIgnoreReturnValue
public static int checkElementIndex(int index, int size) {
return checkElementIndex(index, size, "index");
}
//检查index是否处于[0, size)之间,否则抛出异常(带提示信息)
@CanIgnoreReturnValue
public static int checkElementIndex(int index, int size, @Nullable String desc) {
if (index >= 0 && index < size) {
return index;
} else {
throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
}
}
//检查index是否处于[0, size]之间,否则抛出异常(带提示信息)
@CanIgnoreReturnValue
public static int checkPositionIndex(int index, int size, @Nullable String desc) {
if (index >= 0 && index <= size) {
return index;
} else {
throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
}
}
//检查size是否处于[start,end]中,否则抛出异常(带提示信息)
public static void checkPositionIndexes(int start, int end, int size) {
if (start < 0 || end < start || end > size) {
throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
}
}
下面这个方法怎么用呢?errorMessageTemplate字符串代表异常的描述信息,里面可以包含若干个%s,%s最后会被紧随其后的参数代替。
比如 Preconditions.checkArgument(a > b, "a必须大于b,且a的范围必须是%s到%s",10,100);
public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(Strings.lenientFormat(errorMessageTemplate, errorMessageArgs));
}
}