Validation 注解
文章目录
JSR
JSR (java Specification Requests) java规范提案,是指向JCP提出新增一个标准化技术规范的请求。任何人都可以提交JSR,向Java平台新增API和服务。
JCP
JCP (java Community Process) 是一个开放的国际组织,主要由java开发者以及被授权者组成,职能是发展和更新JCP维护的规范,规范包括J2ME,J2SE,J2EE,XML,OSS,JAIN 等。组织成员可以提交JSR,通过特定程序后,可将提交内容维护到到下一版本的规范里面。
JSR303
JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面,就可以在需要校验的时候进行校验了。
Bean Validation
Bean Validation 是一个运行时的数据验证框架,在验证之后的错误信息会被马上返回。它为JavaBean定义了相应的元数据模型和API。在应用程序中,通过使用Bean Validation或是你自己定义的constraint,就可以确保数据模型(JavaBean) 的正确性。constraint 可以附加到字段,getter方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的constraint。(实现基于JSR303框架提供的API以及服务)
Hibernate Validation
Hibernate Validation 是Bean Validation 的参考实现。它提供了JSR303 规范中所有内置Constraint的实现,除此之外还有一些附加的constraint。
maven 依赖包
<!--jsr 303-->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<!-- hibernate validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.0.Final</version>
</dependency>
<!-- springboot 集成包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Constraint 详解
Bean validation
Constraint | 详细信息 |
---|---|
@Null | 被注释的元素必须为 null |
@NotNull | 被注释的元素不能为 null |
@AssertTrue | 被注释的元素必须为 true |
@AssertFalse | 被注释的元素必须为 false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size | 被注释的元素大小必须在指定的范围内 |
@Digits(integer,fraction) | 被注释的元素必须是一个小数,且整数的位数不能超过integer,小数部分的位数不能超过fraction |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Hibernate Validator 附加 constraint
Constraint | 详细信息 |
---|---|
被注释的元素必须是电子邮箱 | |
@Length | 被注释的字符串的大小必须在指定范围内 |
@NotEmpty | 被注释的字符串非空 |
@Range(min=,max=,message=) | 被注释的元素必须在合适范围内 |
@NotBlank | 验证字符串非null,且长度必须大于0 |
Valid 和 Validated 的区别
- @Valid 可以用在方法,构造函数,方法参数和成员属性(字段)上
- @Validated 可以用在类型,方法和参数上,但是不能用在成员属性上
- @Valid 支持嵌套检测,@Validated 不支持嵌套检测
- @validated 提供分组功能,@Valid 没有分组功能
- @Valid 是使用Hibernate Validation 的时候使用
说明:java的JSR303声明了@Valid这类接口,而Hibernate-validator对其进行了实现。 - @validated 是Spring Validation 校验机制使用,是 @Valid 的一次封装
组序列
默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要。
例:
- 第二个组中的约束验证依赖于一个稳定的状态来运行,而这个稳定的状态是由第一个组来进行验证的。
- 某个组的验证比较耗时,CPU和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。
一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前面的组验证失败,则后面的组将不再给予验证。
代码实现
定义分组接口:
public interface FirstConstraintGroup {
}
public interface SecondConstraintGroup {
}
import javax.validation.GroupSequence;
@GroupSequence({FirstConstraintGroup.class,SecondConstraintGroup.class})
public interface Group {
}
实体类字段增加组序列:
/**
* 业务类型
*/
@NotNull(groups={FirstConstraintGroup.class},message = "BTYPE 不能为空")
@Size(groups={SecondConstraintGroup.class},max = 4, min = 4, message = "BTYPE 的长度不符合业务规定")
String bType;
接口类增加组序列:
@PostMapping("open")
public Object openMonthCard(@RequestBody @Validated({Group.class}) InputJsonVo inputJsonVo) {
OutputParams outputParams = cardAccountMonthService.openCardAccount(inputJsonVo, JSONUtil.toBean(inputJsonVo.getInputParamVo(), OpenCardInputVo.class));
return Util.GenOutPutJson(outputParams, new OutputJsonVo(inputJsonVo.getbType(), inputJsonVo.getTradeNo()));
}
自定义校验注解
代码实现
创建 Annotation:
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 自定义参数校验注解
* 校验 List 集合中是否有null 元素
*/
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHasNullValidatorImpl.class)
public @interface ListNotHasNull {
/**
* 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
*/
int value() default 0;
String message() default "List集合中不能含有null元素";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
创建实现类: (需继承 ConstraintValidator<A extends Annotation, T> 接口)
import org.springframework.stereotype.Component;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
@Component
public class ListNotHasNullValidatorImpl implements ConstraintValidator<ListNotHasNull, List> {
private int value;
@Override
public void initialize(ListNotHasNull constraintAnnotation) {
// 传入value 值,可以在校验中使用
this.value = constraintAnnotation.value();
}
@Override
public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
for (Object object : list) {
if (object == null) {
// 如果List集合中含有Null元素,校验失败
return false;
}
}
return true;
}
}
实体类的使用:
/**
* 测试modelList
*/
@ListNotHasNull(message = "testModelList 中不能含有Null 元素")
List<Long> testModelList;
基于Validation 注解 对于泛型类型不支持的解决办法
我们知道javax的@Valid 注解的效果是递归下去的,可以实现嵌套检测,但是如果实体类中使用了泛型,则Validation 注解就不生效了。因为javax的校验实现
ValidatorImpl.validate(T object, Class<?>... groups)
(是hibernate包里的),读取的是最外层类型的编译时结构,而非运行时类型,所以泛型就失效了。
解决方案是在泛型类型的字段上面加上 @valid 注解。(jakarta.validation-api-2.0.2.jar 版本)
/** 搜索条件 */
@Valid
private T searchReq;
其他方案参考:
@Valid 无法校验List<E>