数据校验是Web开发中必不可少的一环,当一个Http请求发出开始,从前端到后端的控制层,再到业务层,再到数据访问层,最终到达数据库,这其中的每一环,都需要数据校验,可见其重要性。
因为,每一层都需要有进行数据校验的要求,但是,我们不可能真的在每一层都进行一遍数据校验,而是会在控制层或者业务层完成需要的数据校验即可。
一般会将请求参数封装为一个Model对象,然后对该对象进行数据校验接口,如果校验没有问题,就可以沿着工作流,不断传递下去,后续也就不需要进行数据校验了。
但是由于大型程序通常都是会分层的,不同的层如果由不同的程序员来开发的话,就不免会写多套数据校验逻辑,这样就会导致代码冗余,出现很多如下所示的重复代码:
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");
...
}
这其实就是传统的数据校验逻辑,它的缺点如下:
怎么解决呢?
这里先给出一个简单的例子:
public Boolean updateStu(Stu stu){
stuDao.update(stu);
return true;
}
可以看到,上面并没有对stu对象的校验,那么将数据校验逻辑和域对象Model绑定,这是怎么完成的呢?
有很多种方法,这里给出一个最为easy的实现,如下:
@Data
public class Stu {
Integer num;
String name;
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
if(num<0){
throw new IllegalArgumentException("num不能小于0");
}
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
if(name!=null && !name.isEmpty()){
throw new IllegalArgumentException("name不能为空");
}
this.name = name;
}
}
只需求确保请求参数绑定到Model对象的过程是通过调用Model对象的setter方法完成的即可。
但是,上面这种写法也存在诸多的问题,例如: 数据校验的异常异常没有统一化,返回的错误结果格式也不统一,那么还能怎么优化呢?
下面就来看看Java 官方为我们提供的Bean Validation数据校验体系吧 !
Jakarta Bean Validation不仅仅是一个规范,它还是一个生态。
之前名为Java Bean Validation,2018年03月之后就得改名叫Jakarta Bean Validation,Bean Validation技术隶属于Java EE规范.
Bean Validation是标准,它的参考实现除了有我们熟悉的Hibernate Validator外还有Apache BVal,但是后者使用非常小众,忘了它吧。实际使用中,基本可以认为Hibernate Validator是Bean Validation规范的唯一参考实现,是对等的。
这个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个):
所有注解均可标注在:方法、字段、注解、构造器、入参等几乎任何地方.
但是,以上所有注解对null是免疫的,也就是说如果你的值是null,是不会触发对应的校验逻辑的(也就说null是合法的),当然@NotNull / @Null除外.
该规范是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 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后就彻底摒弃了老旧版本
当下主流版本,也就是我们所说的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了:
此版本具有很重要的现实意义,它主要提供如下亮点:
支持通过注解参数化类型(泛型类型)参数来验证容器内的元素,如:List<@Positive Integer> positiveNumbers
更灵活的集合类型级联验证;例如,现在可以验证映射的值和键,如:Map<@Valid CustomerType, @Valid Customer> customersByType
支持java.util.Optional类型,并且支持通过插入额外的值提取器来支持自定义容器类型
让@Past/@Future注解支持注解在JSR310时间上
新增内建的注解类型(共9个):@Email, @NotEmpty, @NotBlank, @Positive, @PositiveOrZero, @Negative, @NegativeOrZero, @PastOrPresent和@FutureOrPresent
所有内置的约束现在都支持重复标记
使用反射检索参数名称,也就是入参名,详见这个API:ParameterNameProvider—很明显这是需要Java 8的启动参数支持的
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
JDK最低版本要求:JDK 8
Hibernate Validator自6.x版本开始对JSR 380规范提供完整支持,除了支持标准外,自己也做了相应的优化,比如性能改进、减少内存占用等等,因此用最新的版本肯定是没错的,毕竟只会越来越好嘛。
新增注解:
相较于1.x版本,2.0版本在其基础上新增了9个实用注解,总数到了22个。现对新增的9个注解解释如下:
导入实现包:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
校验Java Bean:
书写JavaBean和校验程序(全部使用JSR标准API哦):
@Data
public class Stu {
@Min(value = 0)
Integer num;
@NotNull
String name;
}
@Test
public void testBeanValidator(){
Stu stu = new Stu();
stu.setNum(-1);
// 1、使用【默认配置】得到一个校验工厂 这个配置可以来自于provider、SPI提供
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
// 2、得到一个校验器
Validator validator = validatorFactory.getValidator();
// 3、校验Java Bean(解析注解) 返回校验结果
Set<ConstraintViolation<Stu>> result = validator.validate(stu);
// 输出校验结果
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>
添加好后,再次运行程序,控制台正常输出校验失败的消息:
num 最小不能小于0: -1
name 不能为null: null
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://cjdhy.blog.csdn.net/article/details/125939524
内容来源于网络,如有侵权,请联系作者删除!