@Valid注解是什么
用于验证被注解对象是否符合要求,当不符合要求时就会在方法中返回message的错误提示信息。
自定义注解
@Target({ElementType.FIELD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = XXXValidator.class)
@Repeatable(CheckXXX.List.class)
public @interface CheckXXX {
String name() default "abc";
int age() default 1;
String message() default "111";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
CheckXXX[] value();
}
}
@Target:值只能是枚举类ElementType,有以下一些值:
- :允许被修饰的注解作用在类、接口和枚举上
- :允许作用在属性字段上
- :允许作用在方法上
- :允许作用在方法参数上
- :允许作用在构造器上
- ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
- ElementType.ANNOTATION_TYPE:允许作用在注解上
- :允许作用在包上
@Documented:只能用在注解上,如果一个注解@B,被@Documented标注,那么被@B修饰的类,生成文档时,会显示@B。如果@B没有被@Documented标注,最终生成的文档中就不会显示@B。这里的生成文档指的JavaDoc文档!
@Retention:定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中(生命周期长度 SOURCE < CLASS < RUNTIME ,前者能作用的地方后者一定也能作用。一般如果需要 在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要 在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果 只是做一些检查性的操作,比如@Override和@SuppressWarnings,则 可选用 SOURCE 注解):
- source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略。
- class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
- runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。
@Constraint:限定自定义注解的方法。
@Repeatable:被元注解@Repeatable修饰的注解,可以在同一个地方使用多次。使用要点:
- 在需要重复使用的注解上修饰 @Repeatable。
- @Repeatable中的参数为被修饰注解的容器的类对象(class对象)。
- 容器包含一个value方法,返回一个被修饰注解的数组。
校验器
public class XXXValidator implements ConstraintValidator<CheckXXX, Object> {
private String name;
private int age;
@Override
public void initialize(CheckTimeInterval constraintAnnotation) {
this.name = constraintAnnotation.name();
this.age = constraintAnnotation.age();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
BeanWrapper beanWrapper = new BeanWrapperImpl(o);
String name = (String)beanWrapper.getPropertyValue(name);
int age = (Integer)beanWrapper.getPropertyValue(age);
return age > 10 && name.equals("abc");
}
}
ConstraintValidator<>:第二个参数表示注解修饰的对象类型。(类注解就是Object)
initialize():类初始化方法。
isValid():返回true表示校验通过,返回false表示校验失败,返回message的错误提示信息。
示例
使用一次的注解:
@Target({ ElementType.FIELD })
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { VinValidator.class })
public @interface CheckVins {
String message() default "格式错误,输入多行vin必须为17位!";
int max();
int itemLength();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class VinValidator implements ConstraintValidator<CheckVins, String[]> {
private int max;
private int itemLength;
@Override
public void initialize(CheckVins constraintAnnotation) {
max = constraintAnnotation.max();
itemLength = constraintAnnotation.itemLength();
}
@Override
public boolean isValid(String[] value, ConstraintValidatorContext context) {
//没有该参数时不判断
if(value == null){
return true;
}
//最多输入200个vin
else if (value.length > max) {
return false;
}
//输入vin大于一个时,要求每个必须为17位
if (value.length>1){
for (String item : value) {
if (item.length() != itemLength) {
return false;
}
}
}
return true;
}
}
public class RequestDownloadRecord {
...
@CheckVins(max = 200, itemLength = 17)
private String[] vin;
}
重复使用的注解:
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckTimeIntervalValidation.class)
@Documented
@Repeatable(CheckTimeInterval.List.class)
public @interface CheckTimeInterval {
/**
* 开始日期
* @return field
*/
String beginTime() default "startTime";
/**
* 查询参数时间类型
* @return field
*/
String timeType() default "LocalDateTime";
/**
* 结束日期
* @return field
*/
String endTime() default "endTime";
/**
* 日期间隔
* @return field
*/
int dayRange() default 30;
String message() default "{.}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
CheckTimeInterval[] value();
}
}
public class CheckTimeIntervalValidation implements ConstraintValidator<CheckTimeInterval, Object> {
private String beginTime;
private String endTime;
private int dayRange;
private String timeType;
@Override
public void initialize(CheckTimeInterval constraintAnnotation) {
this.beginTime = constraintAnnotation.beginTime();
this.endTime = constraintAnnotation.endTime();
this.dayRange = constraintAnnotation.dayRange();
this.timeType=constraintAnnotation.timeType();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
BeanWrapper beanWrapper = new BeanWrapperImpl(o);
long result=0;
if ("LocalDateTime".equals(timeType)){
LocalDateTime begin = (LocalDateTime) (beanWrapper.getPropertyValue(beginTime));
LocalDateTime end = (LocalDateTime) (beanWrapper.getPropertyValue(endTime));
if (null == begin || null == end) {
return false;
}
// result = (begin);
result=begin.until(end, ChronoUnit.DAYS);
}else if ("LocalDate".equals(timeType)){
LocalDate begin = (LocalDate) (beanWrapper.getPropertyValue(beginTime));
LocalDate end = (LocalDate) (beanWrapper.getPropertyValue(endTime));
if (null == begin || null == end) {
return false;
}
result=begin.until(end, ChronoUnit.DAYS);
}
return result>=0&&result <= dayRange;
}
}
@CheckTimeInterval(beginTime = "start",endTime = "end",timeType="LocalDateTime",dayRange = 14,message = "开始时间必须小于结束时间,只能查询14天的日期范围数据")
@CheckTimeInterval(beginTime = "start",endTime = "end",timeType="LocalDateTime",dayRange = 28,message = "开始时间必须小于结束时间,只能查询28天的日期范围数据")
public class RunningRecordQuery {
...
}