Java EE: Bean Validation
Jakarta EE Validation API
简介
Bean Validation有多套标准,最早是JSR 303的Bean Validation 1.0,现今流行的是Java EE 8的Bean Validation 2.0,即JSR 380标准Bean Validation通过注解作用于属性字段上,在运行时进行验证需要说明的是,
JPA规范的@Id、@UniqueConstraint等是在生成SQL时额外添加约束,这个约束是数据库层面上的,因此只有在保存、更新、删除时会检查值的有效性而
Bean Validation作用于Java内存中,给标识符赋值的时候,例如@NotNull注解规定该标识符不能被赋予null,这种约束的检查是在JVM内部进行的,没有用到外部的软件添加
Bean Validation,有助于在更早的时候发现问题,而对于没有问题的操作,Validation的开销并不大,对于Validation无法发现的问题,数据库的约束则是最后一层保障接口依赖:
1
2
3
4
5<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
</dependency>hibernate实现(包含接口依赖):1
2
3
4
5<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>可能使用到
el表达式
验证注解
验证的通用属性
message:验证失败时,将自动抛出ConstraintViolationException或MethodArgumentNotValidException异常,它们会包含该messagemessage支持有限的EL表达式,默认会是{jakarta.validation.constraints.ConstrantName.message}这个EL表达式,是配置文件里设置的groups:指定Class<?>[],表示这个验证注解所属的分组类(可有多组)在验证时,使用
validate(bean, Class...),只会检验bean中属于Class的验证注解的字段默认为
jakarta.validation.groups.Default,validate()若不指定组则默认检验属于Default组的验证payload:指定Class<? extends Payload>[],定义payload后,发生异常时可从异常中通过getPayload()获取payload信息通常用于定义一些元数据,例如便于分类地处理异常,例如按严重程度、按业务逻辑
@Xxx.List子注解:指定@Xxx(同类型验证注解)数组,便于对同一类注解但不同的验证条件赋予不同的message
空值安全
- 只有以下注解会针对
null,其它注解将始终视null为合法的 @NotNull/@Null:针对任意Object,不允许/必须为null@NotBlank:针对任意CharSequence,必须不为null且必须包含非空白字符,即不为空、不含Unicode空白字符(空格、制表符、换行符、回车符、换页符)@NotEmpty:针对任意CharSequence、Collection、Map、Array,必须不为null且不为空
数值检查
@AssertTrue/@AssertFalse:针对boolean或Boolean,必须为True/False@Min(value)/@Max(value):针对BigDecimal,BigInteger,byte、short、int、long以及它们的包装类,检查其最小值/最大值,value值是longdouble和float因为取整误差,不被支持设置@Min和@Max@DecimalMin(value)/@DecimalMax(value):类似@Min(value)/@Max(value),但是value是String,会被转化为BigDecimal表示此外,额外含有一个
inclusive布尔属性,表示是否包含边界@Negative/@NegativeOrZero/@Positive/@PositiveOrZero:针对BigDecimal,BigInteger,byte、short、int、long、float、double以及它们的包装类,检查其是否为负/非正/正/非负数,因为只需要判零以及符号,所以支持float和double@Digits(integer, fraction):针对BigDecimal,BigInteger,byte、short、int、long以及它们的包装类,以及任意的CharSequence,检查整数位数<= integer,小数位数<= fraction@Size(min = 0, max = Integer.MAX_VALUE):针对任意CharSequence、Collection、Map、Array,其元素个数必须在[min, max]内,允许为null
特殊类型
@Pattern(regexp, flags = {}):针对任意CharSequence,检查其是否符合java.util.regex.Pattern的检查regexp与java.util.regex.Pattern的规则一致,flags则是@Flag[],表示检查模式,与java.util.regex.Pattern内部定义的标志位一致,以注解形式成为元数据的一部分@Email(regexp = ".*", flags = {}):提供一个默认的邮箱正则表达式规则,可以使用自定义的regexp覆盖原有表达式@URL(protocal="", host="", port=-1, regexp=".*", flags={}):针对任意CharSequence,检查其是否符合java.net.URL的构造方法的验证,可自定义协议名称、主机名称、端口号,以及可以覆盖@Pattern提供的regexp和flags来自定义匹配规则@Future/@FutureOrPresent/@Past/@PastOrPresent:针对任意java.time以及java.time.chrono的日期/时间类对象,检查是否是未来/未来或现在/过去/过去或现在的时间,Now这个对象基于JVM以及现在的时区来获取,具体到时间这里的
Present的含义会因为针对的类型而变化,例如针对Year对象,Present视整一年为合法的“现在”,而不需要完全和Now一致@Valid:空注解,针对任意类型的对象,可以注解一个方法、一个方法的参数、一个类的属性,表示对返回值、参数值、属性值进行级联验证,即检查到这个对象时,会同时检查该对象的所有属性,但只会级联一层,不会深入到属性的属性(除非又有@Valid),例如@Valid List<@Valid ElementType>,这样才会级联到ElementType的属性
验证器
所有注解在针对方法参数以及方法时,必须依赖
Spring的IoC容器或其它CDI框架的实现,才是有用的(能自动校验)否则,在一般的
Hibernate或其它实现中,必须依靠验证器,由开发者在代码中显式地校验Validator接口:validate(T obj, Class<?>... groups):验证obj的所有属性validateProperty(T obj, String pName, Class<?>...):验证其特定属性validateValue(T obj, String pName, Object value, Class<?>...):验证value是否符合其某个字段的约束
ConstraintViolation<T>接口:所有上述验证方法的返回值是Set<ConstraintViolation>,表示出现冲突的约束信息getMessage():获取错误信息getRootBean():获取根对象getLeafBean():获取出错的对象getConstraintDescriptor():获取约束描述符
ConstraintDescriptor:约束描述符getPayload():获取payload