数据校验是一个比较常见的工作,在日常的开发中贯穿于代码的各个层次,虽然简单,但是却是一个繁琐的事。一般来说我们的第一反应肯定是使用if判断参数,然后校验不通过打日志抛异常,可能还会根据业务来归纳各种各样的Util或者Rule。虽然这种做法可以达到效果,但是代码散乱,可读性可维护性差。所以有什么办法可以解决掉这些烦人的if操作呢?
参数校验作为一个痛点,Java业界推出了Bean Validation。
Bean Validation是Java定义的一套基于注解的数据校验规范,目前已经从JSR 303的1.0版本升级到JSR 349的1.1版本,再到JSR 380的2.0版本(2.0完成于2017.08),已经经历了三个版本。
它定义了一套元数据模型和API对JavaBean实现校验,默认是以注解作为元数据,可以通过XML重写或者拓展元数据,通常来说注解的方式可以实现比较简单逻辑的校验,而复杂校验就需要通过XML来描述。可以说Bean Validation是JavaBean的一个拓展,也就是说它布局于哪一层的代码,不局限于Web应用还是端应用。
Bean Validation 2.0 关注点
1.使用Bean Validation的最低Java版本为Java 8
2.支持容器的校验,通过TYPE_USE类型的注解实现对容器内容的约束:List<@Email String>
3.支持日期/时间的校验,@Past和@Future
4.拓展元素数据:@Email,@NotEmpty,@NotBlank,@Positive, @PositiveOrZero,@Negative,@NegativeOrZero,@PastOrPresent和@FutureOrPresent
Bean Validation的实现
Bean Validation在2.0之前有两个官方认可的实现:Hibernate Validator和Apache BVal,但如果你想用2.0版本的话,基本上只有Hibernate Validator,而这里我使用的是Hibernate Validator,其他实现不做展开。
使用
安装依赖:
根据jsr380规范,validation-api依赖库包含标准validation api:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.0.Final</version>
</dependency>
验证API参考实现
Hibernate Validator是验证规范的参考实现,我们需要增加下面依赖:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>6.0.2.Final</version>
</dependency>
需要注意的是,hibernate-validator与Hibernate持久化功能完全独立,这里并没有引入持久化方面的库。
表达式依赖
jsr380提供了变量插入功能,允许表达式在错误信息中混合使用。为了解析表达式,我们必须增加下面依赖:表达式语言API及其实现,GlassFish提供了参考实现:
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b09</version>
</dependency>
如果没有增加表达式库,会报错,信息如下:
HV000183: Unable to load ‘javax.el.ExpressionFactory’. Check that you have the EL dependencies on the classpath, or use ParameterMessageInterpolator instead
注意如果你是Tomcat7的话,在tomcat的lib下也有一个el-api.jar,该版本的jar包版本较低,直接运行会产生冲突报错,它会优先使用tomcat里的jar包而不是项目引入的,所以需要用项目引入的jar包来替换保持版本一致,或者升级tomcat版本到8即可解决问题。如果是通过RPM打包的方式可以写一个后置脚本来替换jar包,可以省去手动替换包。
小Demo
User类:
@Data
public class User {
private Address address;
@NotBlank
private String name;
private String gender;
@Positive
private int age;
private List<@Email String> emails;
}
Main函数:
User user = new User();
user.setAddress(new Address(new Country("China")));
user.setGender("man");
user.setAge(-1);
user.setEmails(Arrays.asList("sevenlin@gmail.com", "sevenlin.com"));
Set<ConstraintViolation<User>> result = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(user);
List<String> message = result.stream()
.map(v -> v.getPropertyPath() + " " + v.getMessage() + ": " + v.getInvalidValue())
.collect(Collectors.toList());
message.forEach(System.out::println);
输出结果:
name 不能为空: null
emails[1].<list element> 不是一个合法的电子邮件地址: sevenlin.com
age must be greater than 0: -1
参考:
https://zhuanlan.zhihu.com/p/42818634
https://blog.csdn.net/neweastsun/article/details/79602322
http://imushan.com/2018/01/18/java/language/%E6%8E%8C%E6%8F%A1Java-Bean-Validation/