✍前言
你好,我是YourBatman。
作为一个开发者,聊起数据校验(Bean Validation),不管是前、中、后端都耳熟能详,并且心里暗爽:so easy。
的确,对数据做校验是一个程序员的基本素质,它不难但发生在我们程序的几乎每个角落,就像下面这幅图所示:每一层都需要做校验。
如果你真的这么去写代码的话(每一层都写一份),肯定是不太合适的,良好的状态应该如下图所示:
作为一个Java开发者,在Spring大行其道的今天,很多小伙伴了解数据校验来自于Spring MVC场景,甚至止步于此。殊不知,Java EE早已把它抽象成了JSR标准技术,并且Spring还是借助整合它完成了自我救赎呢。
在我看来,按Spring的3C战略标准来比,Bean Validation数据校验这块是没有能够完成对传统Java EE的超越,自身设计存在过重、过度设计等特点。
本专栏命名为Bean Validation(数据校验),将先从JSR标准开始,再逐渐深入到具体实现Hibernate Validation、整合Spring使用场景等等。因此本专栏将让你将得到一份系统数据校验的知识。
✍正文
在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。应用程序必须通过某种手段来确保输入进来的数据从语义上来讲是正确的,比如生日必须是过去时,年龄必须>0等等。
为什么要有数据校验?
数据校验是非常常见的工作,在日常的开发中贯穿于代码的各个层次,从上层的View层到后端业务处理层,甚至底层的数据层。
我们知道通常情况下程序肯定是分层的,不同的层可能由不同的人来开发或者调用。若你是一个有经验的程序员,我相信你肯定见过在不同的层了都出现了相同的校验代码,这就是某种意义上的垃圾代码:
public String queryValueByKey(String zhName, String enName, Integer age) { checkNotNull(zhName, "zhName must be not null"); checkNotNull(enName, "enName must be not null"); checkNotNull(age, "age must be not null"); validAge(age, "age must be positive"); ... }
从这个简单的方法入参校验至少能发现如下问题:
- 需要写大量的代码来进行参数基本验证(这种代码多了就算垃圾代码)
- 需要通过文字注释来知道每个入参的约束是什么(否则别人咋看得懂)
- 每个程序员做参数验证的方式可能不一样,参数验证抛出的异常也不一样,导致后期几乎没法维护
如上会导致代码冗余和一些管理的问题(代码量越大,管理起来维护起来就越困难),比如说语义的一致性问题。为了避免这样的情况发生,最好是将验证逻辑与相应的域模型进行绑定,这就是本文将要提供的一个新思路:Bean Validation。
关于Jakarta EE
2018年03月, Oracle 决定把 JavaEE 移交给开源组织 Eclipse 基金会,并且不再使用Java EE这个名称。这是它的新logo:
对应的名称修改还包括:
JCP 将继续支持 Java SE社区。 但是,Jakarta EE规范自此将不会在JCP下开发。Jakarta EE标准大概由Eclipse Glassfish、Apache TomEE、Wildfly、Oracle WebLogic、JBoss、IBM、Websphere Liberty等组织来制定
迁移
既然名字都改了,那接下来就是迁移喽,毕竟Java EE这个名称(javax包名)不能再用了嘛。Eclipse接手后发布的首个Enterprise Java将是 Jakarta EE 9,该版本将以Java EE 8作为其基准版本(最低版本要求是Java8)。
有个意思的现象是:Java EE 8是2019.09.10发布的,但实际上官方名称是Jakarta EE 8了。很明显该版本并非由新组织设计和制定的,不是它们的产物。但是,彼时平台已更名为Jakarta有几个月了,因此对于一些Jar你在maven市场上经常能看见两种坐标:
<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>
虽然坐标不一样,但是内容是100%一样的(包名均还为javax.*),很明显这是更名的过度期,为后期全面更名做准备呢。
严格来讲:只要大版本号(第一个数字)还一样,包名是不可能变化的,因此一般来说均具有向下兼容性
既然Jakarta释放出了更名信号,那么下一步就是彻彻底底的改变喽。果不其然,这些都在Jakarta EE 9里得到实施。
Jakarta EE 9
2020.08.31,Jakarta后的第一个企业级平台Jakarta EE 9正式发布。如果说Jakarta EE 8只是冠了个名,那么这个就名正言顺了。
小贴士:我写本文时还没到2020.08.31呢,这个时间是我在官网趴来的,因此肯定准确
这次企业平台的升级最大的亮点是:
- 把旗下30于种技术的大版本号全部+1(Jakarta RESTful Web Services除外)
- 包名全部去javax.*化,全部改为jakarta.*
- JavaSE基准版本要求依旧保持为Java 8(而并非Java9哦)
可以发现本次升级的主要目的并着眼于功能点,仍旧是名字的替换。虽然大家对Java EE的javax有较深的情节,但旧的不去新的不来。我们以后开发过中遇到jakarta.*这种包名就不用再感到惊讶了,提前准备总是好的。
Jakarta Bean Validation
Jakarta Bean Validation不仅仅是一个规范,它还是一个生态。
之前名为Java Bean Validation,2018年03月之后就得改名叫Jakarta Bean Validation
喽,这不官网早已这么称呼了:
Bean Validation技术隶属于Java EE规范,期间有多个JSR(Java Specification Requests
)支持,截止到稿前共有三次JSR标准发布
说明:JCP这个组织就是来定义Java标准的,在Java行业鼎鼎有名的公司大都是JCP的成员,可以共同参与Java标准的制定,影响着世界。包括掌门人Oracle以及Eclipse、Redhat、JetBrains等等。值得天朝人自豪的是:2018年5月17日阿里巴巴作为一员正式加入JCP组织,成为唯一一家中国公司。
Bean Validation是标准,它的参考实现除了有我们熟悉的Hibernate Validator外还有Apache BVal,但是后者使用非常小众,忘了它吧。实际使用中,基本可以认为Hibernate Validator是Bean Validation规范的唯一参考实现,是对等的。
小贴士:Apache BVal胜在轻量级上,只有不到1m空间所以非常轻量,有些选手还是忠爱的(此项目还在发展中,并未停更哦,有兴趣你可以自己使用试试)
JSR303
这个JSR提出很早了(2009年),它为 基于注解的 JavaBean验证定义元数据模型和API,通过使用XML验证描述符覆盖和扩展元数据。JSR-303主要是对JavaBean进行验证,如方法级别(方法参数/返回值)、依赖注入等的验证是没有指定的。
作为开山之作,它规定了Java数据校验的模型和API,这就是Java Bean Validation 1.0版本。
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.0.0.GA</version> </dependency>
该版本提供了常见的校验注解(共计13个):
所有注解均可标注在:方法、字段、注解、构造器、入参等几乎任何地方
可以看到这些注解均为平时开发中比较常用的注解,但是在使用过程中有如下事项你仍旧需要注意:
1.以上所有注解对null是免疫的,也就是说如果你的值是null,是不会触发对应的校验逻辑的(也就说null是合法的),当然喽@NotNull / @Null除外
2.对于时间类型的校验注解(@Future/@Past),是开区间(不包含相等)。也就是说:如果相等就是不合法的,必须是大于或者小于
- 这种case比较容易出现在LocalDate这种只有日期上面,必须是将来/过去日期,当天属于非法日期
3.@Digits它并不规定数字的范围,只规定了数字的结构。如:整数位最多多少位,小数位最多多少位
4.@Size规定了集合类型的范围(包括字符串),这个范围是闭区间
5.@DecimalMax和@Max作用基本类似,大部分情况下可通用。不同点在于:
@DecimalMax设置最大值是用字符串形式表示(只要合法都行,比如科学计数法),而@Max最大值设置是个long值
- 我个人一般用@Max即可,因为够用了~
另外可能有人会问:为毛没看见@NotEmpty、@Email、@Positive等常用注解?那么带着兴趣和疑问,继续往下看吧~