Bean Validation对象验证标准
words: 2k views: time: 8minJava’s standard for object validation最早在Java EE6中提出,作为Bean Validation 1.0(JSR-303)。它定义了一种在Java对象上执行声明性验证的方式,提供了一个运行时的数据验证框架。更多详细内容可以参考官网:https://beanvalidation.org
Bean Validationn可以让代码变得更简洁清晰,让开发人员在定义数据模型时不必考虑实现框架的限制。当然它不止提供了一些基本的constraint,也可以自定义验证规则,在实际的开发中,可以根据自己的需要组合或开发出更加合适的constraint。
依赖
validation-api主要提供了一套用于在Java Bean对象上执行声明性验证的标准接口和注解,但不包含具体实现,而是留给不同的厂商或项目来提供实现
1 | <dependency> |
hibernate-validator是Bean Validation规范的一个实现,它实现了validation-api中定义的接口和功能
1 | <dependency> |
一般在基于springboot开发的Java应用中,只需添加对应的starter依赖即可。spring-boot-starter-web在2.3.0版本之后取消了集成,需要自己添加依赖
1 | <dependency> |
@Valid与@Validated
javax.validation.Valid是标准规范validation-api提供的注解,而org.springframework.validation.annotation.Validated是Spring自定义的一个注解,在spring-context包中,其增强了分组功能,作为JSR-303在spring中的一个变体。它们都可以对方法和参数进行校验,两者可以兼容,但也有一些区别:
区别 | @Valid | @Validated |
---|---|---|
提供者 | JSR-303规范 | spring |
分组验证 | 不支持 | 支持 |
嵌套验证 | 支持 | 不支持 |
标记位置 | CONSTRUCTOR, FIELD, TYPE_USE, METHOD, PARAMETER | TYPE, METHOD, PARAMETER |
spring中的Validated还是基于Aop来增强实现的,MethodValidationPostProcessor在Bean的初始化完成之后,会判断类是否被@Validated标记,然后MethodValidationInterceptor会拦截所有方法,执行校验逻辑。最后委派给Validator执行参数和返回值校验,并得到ConstraintViolation进行处理。
实践
参数校验
关于参数校验,用的比较多的应该是在spring mvc的Controller中,用来对接口请求参数进行验证。如下所示:
- 在Controller上标记@Validated,针对Get类的请求,对参数直接进行校验;
- 如果是复合对象参数,需要在参数前标记@Validated或@Valid,然后再在对应的类属性上标记校验注解;
1 |
|
其实在Service接口层声明参数校验也是一种比较好的方式,这样更方便对返回值进行校验,但这里需要使用@Valid声明,@Validated无效
1 | public interface InfoService { |
然后在对应的实现类上需要标记@Validated
1 |
|
分组校验
分组校验主要针对接口参数实体类复用的场景,比如同样一个属性,在这个接口不需要校验,而在另一个接口又需要校验。一种办法是使用VO、BO、DO那种方式将不同接口的参数类完全分开,这样就避免了复用问题。但我们不推荐这种方式,容易导致类型泛滥,我们希望定义尽量精简的类型,如果这样,那么可以使用groups对声明的校验规则进行分组
1 |
|
然后在声明@Validated也需要指定对哪些分组生效
1 |
|
嵌套校验
嵌套校验比较常见的是一种场景是集合类参数,如果要对集合类参数进行校验,可以使用@Valid声明参数,并在Controller上声明@Validated
1 |
|
有的场景下,需要对复合对象中的一些类型进行校验,那么也可以使用@Valid来声明
1 |
|
自定义校验
这里我们定义一个手机号校验规则
- 定义注解
@Repeatable和List定义可以让该注解在同一个位置重复多次,通常是不同的配置,比如不同的分组和消息
@Constraint指明约束的验证器,需要实现javax.validation.ConstraintValidator接口
payload有效负载,可以通过payload来标记一些需要特殊处理的操作
1 | .class}) (validatedBy = {MobileValidator |
- 定义验证器
验证器的两个类型参数,分别是要验证的注解类,和验证器可以处理的元素类型。
1 | public class MobileValidator implements ConstraintValidator<Mobile, String> { |
附录
Validation 2.0(JSR-380) 相关的22个注解:
非空检查
注解 支持类型 备注 @Null Object 为null @NotNull Object 不为null @NotBlank CharSequence 不为null,且必须有一个非空格字符 @NotEmpty CharSequence, Collection, Map, Array 不为null,且length/size > 0 Boolean判断
注解 支持类型 备注 @AssertTrue boolean, Boolean 为true @AssertFalse boolean, Boolean 为false 日期检查
注解 支持类型 备注 @Future Date、Calendar等 日期为当前时间之后 @FutureOrPresent Date、Calendar等 验证日期为当前时间或之后 @Past Date、Calendar等 日期为当前时间之前 @PastOrPresent Date、Calendar等 验证日期为当前时间或之前 数值检查
注解 支持类型 备注 @Max BigDecimal, BigInteger, byte, short, int, long 小于或等于 @Min BigDecimal, BigInteger, byte, short, int, long 大于或等于 @DecimalMax BigDecimal, BigInteger, byte, short, int, long, CharSequence 小于或等于 @DecimalMin BigDecimal, BigInteger, byte, short, int, long, CharSequence 大于或等于 @Negative BigDecimal, BigInteger, byte, short, int, long, float, double 负数 @NegativeOrZero BigDecimal, BigInteger, byte, short, int, long, float, double 负数或零 @Positive BigDecimal, BigInteger, byte, short, int, long, float, double 正数 @PositiveOrZero BigDecimal, BigInteger, byte, short, int, long, float, double 正数或零 @Digits(integer = 3, fraction = 2) BigDecimal, BigInteger, byte, short, int, long, CharSequence 精度限制,整数位数和小数位上限 其它
注解 支持类型 备注 @Pattern CharSequence 匹配正则表达式 @Email CharSequence 邮箱地址 @Size CharSequence, Collection, Map, Array 大小范围length/size
hibernate-validator的扩展:
注解 | 支持类型 | 备注 |
---|---|---|
@Length | String | 字符串长度范围 |
@Range | 数值类型和String | 指定范围 |
@URL | URL地址验证 |