JSR349
该规范是2013年完成的,伴随着Java EE 7
一起发布,它就是我们比较熟悉的Bean Validation 1.1。
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.1.0.Final</version> </dependency>
相较于1.0版本,它主要的改进/优化有如下几点:
- 标准化了Java平台的约束定义、描述、和验证
- 支持方法级验证(入参或返回值的验证)
- Bean验证组件的依赖注入
- 与上下文和DI依赖注入集成
- 使用EL表达式的错误消息插值,让错误消息动态化起来(强依赖于ElManager)
- 跨参数验证。比如密码和验证密码必须相同
小贴士:注解个数上,相较于1.0版本并没新增~
它的官方参考实现如下:
可以看到,Java Bean Validation 1.1版本实现对应的是Hibernate Validator 5.x(1.0版本对应的是4.x)
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.3.Final</version> </dependency>
当你导入了hibernate-validator后,无需再显示导入javax.validation。hibernate-validator 5.x版本基本已停更,只有严重bug才会修复。因此若非特殊情况,不再建议你使用此版本,也就是不建议再使用Bean Validation 1.1版本,更别谈1.0版本喽。
小贴士:Spring Boot1.5.x默认集成的还是Bean Validation 1.1哦,但到了Boot 2.x后就彻底摒弃了老旧版本
JSR380
当下主流版本,也就是我们所说的Java Bean Validation 2.0和Jakarta Bean Validation 2.0版本。关于这两种版本的差异,官方做出了解释:
他俩除了叫法不一样、除了GAV上有变化,其它地方没任何改变。它们各自的GAV如下:
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> <version>2.0.1</version> </dependency>
现在应该不能再叫Java EE
了,而应该是Jakarta EE
。两者是一样的意思,你懂的。Jakarta Bean Validation 2.0
是在2019年8月发布的,属于Jakarta EE 8
的一部分。它的官方参考实现只有唯一的Hibernate validator了:
此版本具有很重要的现实意义,它主要提供如下亮点:
1.支持通过注解参数化类型(泛型类型)参数来验证容器内的元素,如:List<@Positive Integer> positiveNumbers
1.更灵活的集合类型级联验证;例如,现在可以验证映射的值和键,如:Map<@Valid CustomerType, @Valid Customer> customersByType
2.支持java.util.Optional类型,并且支持通过插入额外的值提取器来支持自定义容器类型
2.让@Past/@Future注解支持注解在JSR310时间上
3.新增内建的注解类型(共9个):@Email, @NotEmpty, @NotBlank, @Positive, @PositiveOrZero, @Negative, @NegativeOrZero, @PastOrPresent和@FutureOrPresent
4.所有内置的约束现在都支持重复标记
5.使用反射检索参数名称,也就是入参名,详见这个API:ParameterNameProvider
1.很明显这是需要Java 8的启动参数支持的
6.Bean验证XML描述符的名称空间已更改为:
1.META-INF/validation.xml -> http://xmlns.jcp.org/xml/ns/validation/configuration
2.mapping files -> http://xmlns.jcp.org/xml/ns/validation/mapping
7.JDK最低版本要求:JDK 8
Hibernate Validator自6.x版本开始对JSR 380规范提供完整支持,除了支持标准外,自己也做了相应的优化,比如性能改进、减少内存占用等等,因此用最新的版本肯定是没错的,毕竟只会越来越好嘛。
新增注解
相较于1.x版本,2.0版本在其基础上新增了9个实用注解,总数到了22个。现对新增的9个注解解释如下:
像@Email、@NotEmpty、@NotBlank
之前是Hibernate额外提供的,2.0标准后hibernate自动退位让贤并且标注为过期了。Bean Validation 2.0的JSR规范制定负责人就职于Hibernate,所以这么做就很自然了。就是他:
小贴士:除了JSR标准提供的这22个注解外,Hibernate Validator还提供了一些非常实用的注解,这在后面讲述Hibernate Validator时再解释吧
使用示例
导入实现包:
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.5.Final</version> </dependency>
校验Java Bean
书写JavaBean和校验程序(全部使用JSR标准API哦):
@ToString @Setter @Getter public class Person { @NotNull public String name; @NotNull @Min(0) public Integer age; }
public static void main(String[] args) { Person person = new Person(); person.setAge(-1); // 1、使用【默认配置】得到一个校验工厂 这个配置可以来自于provider、SPI提供 ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); // 2、得到一个校验器 Validator validator = validatorFactory.getValidator(); // 3、校验Java Bean(解析注解) 返回校验结果 Set<ConstraintViolation<Person>> result = validator.validate(person); // 输出校验结果 result.stream().map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue()).forEach(System.out::println); }
运行程序,不幸抛错:
Caused by: java.lang.ClassNotFoundException: javax.el.ELManager at java.net.URLClassLoader.findClass(URLClassLoader.java:382) at java.lang.ClassLoader.loadClass(ClassLoader.java:418) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ...
上面说了,从1.1版本起就需要El管理器支持用于错误消息动态插值,因此需要自己额外导入EL的实现。
小贴士:EL也属于Java EE标准技术,可认为是一种表达式语言工具,它并不仅仅是只能用于Web(即使你绝大部分情况下都是用于web的jsp里),可以用于任意地方(类比Spring的SpEL)
这是EL技术规范的API:
<!-- 规范API --> <dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version> </dependency>
Expression Language 3.0表达式语言规范发版于2013-4-29发布的,Tomcat 8、Jetty 9、GlasshFish 4都已经支持实现了EL 3.0,因此随意导入一个都可(如果你是web环境,根本就不用自己手动导入这玩意了)。
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> <version>9.0.22</version> </dependency>
添加好后,再次运行程序,控制台正常输出校验失败的消息:
age 最小不能小于0: -1 name 不能为null: null
校验方法/校验构造器
请移步下文详解。
加餐:Bean Validation 3.0
伴随着Jakarta EE 9
的发布,Jakarta Bean Validation 3.0
也正式公诸于世。
<dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> <version>3.0.0</version> </dependency>
它最大的改变,甚至可以说唯一的改变就是包名的变化:
至此不仅GAV上实现了更名,对代码执行有重要影响的包名也彻彻底底的去javax.*化了。因为实际的类并没有改变,因此仍旧可以认为它是JSR380的实现(虽然不再由JCP组织制定标准了)。
参考实现
毫无疑问,参考实现那必然是Hibernate Validator。它的步伐也跟得非常的紧,退出了7.x版本用于支持Jakarta Bean Validation 3.0。虽然是大版本号的升级,但是在新特性方面你可认为是无:
✍总结
本文着眼于讲解JSR规范、Bean Validation校验标准、官方参考实现Hibernate Validator,把它们之间的关系进行了关联,并且对差异进行了鉴别。我认为这篇文章对一般读者来说是能够刷新对数据校验的认知的。
wow,数据校验背后还有这么广阔的天地
数据校验是日常工组中接触非常非常频繁的一块知识点,我认为掌握它并且熟练运用于实际工作中,能起到事半功倍的效果,让代码更加的优雅,甚至还能实现别人加班你加薪呢。所以又是一个投出产出比颇高的小而美专栏在路上…
作为本专栏的第一篇文章以JSR标准作为切入点进行讲解,是希望理论和实践能结合起来学习,毕竟理论的指导作用不可或缺。有了理论铺垫的基石,后面实践将更加流畅,正所谓着地走路更加踏实嘛。