数据校验是一个很常见的工作,它贯穿于各个层次,从展现层到持久层。通常情况下,如果同样的校验逻辑被应用到各个层次的话,非常容易出错,也很耗时。为了避免重复的校验,开发者通常将校验逻辑绑定到模型上;这样做,校验逻辑看起来就像是模型类的元数据一样。
对比下面2张图看一下:


第一张图中,校验逻辑散布在各个层次;第二张图中,校验逻辑集中到模型中。
JSR 380 - Bean Validation 2.0 为数据模型和方法的校验定义了元数据模型和API。
下面,以一个入门案例感性地认识一下。
- 引入依赖,我们只需要引入hibernate-validator就行了,它会间接引入validation-api
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.7.Final</version>
</dependency>
- 注意,如果是在非web环境下,我们还需要引入以下依赖
<dependency>
<!-- 从名字就可以看出来,这个依赖是关于EL表达式的。
具体到这里,这个依赖就是为了做消息提示中的表达式计算的。
比如,国际化消息提示中的 org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}-->
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b08</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-cdi</artifactId>
<version>6.0.7.Final</version>
</dependency>
- 接下来定义一个model,并对其属性应用约束
public class Car {
@NotNull(message = "must not be null")
private String manufacturer;
@NotNull
@Size(min = 2, max = 14, message = "size must be between 2 and 14")
private String licensePlate;
@Min(value = 2, message = "must bigger than 2")
private int seatCount;
public Car(String manufacturer, String licensePlate, int seatCount) {
this.manufacturer = manufacturer;
this.licensePlate = licensePlate;
this.seatCount = seatCount;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getLicensePlate() {
return licensePlate;
}
public void setLicensePlate(String licensePlate) {
this.licensePlate = licensePlate;
}
public int getSeatCount() {
return seatCount;
}
public void setSeatCount(int seatCount) {
this.seatCount = seatCount;
}
}
- 使用Validator的实例验证这些约束
public class CarTest {
private static Validator validator;
@BeforeClass
public static void setUpValidator() {
// 通过工厂获取到一个Validator的实例
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@Test
public void manufacturerIsNull() {
// 只有一个违反了约束,manufacturer 属性不能为空
Car car = new Car(null, "DD-AB-123", 4);
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
// 我们知道,上面创建的Car违反了一个约束
Assert.assertEquals(1, constraintViolations.size());
// 并且这个约束对应的消息我们也知道
Assert.assertEquals("must not be null",
constraintViolations.iterator().next().getMessage());
}
@Test
public void licensePlateTooShort() {
// 只违反了一个约束,licensePlate 属性的长度必须在2和14之间
Car car = new Car("Morris", "D", 4);
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
Assert.assertEquals(1, constraintViolations.size());
Assert.assertEquals("size must be between 2 and 14",
constraintViolations.iterator().next().getMessage());
}
@Test
public void seatCountTooLow() {
// 只违反了一个约束,seatCount 属性的值不能小于2
Car car = new Car("Morris", "DD-AB-123", 1);
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
Assert.assertEquals(1, constraintViolations.size());
Assert.assertEquals("must bigger than 2",
constraintViolations.iterator().next().getMessage());
}
@Test
public void carIsValid() {
// 没有违反约束
Car car = new Car("Morris", "DD-AB-123", 4);
Set<ConstraintViolation<Car>> constraintViolations = validator.validate(car);
Assert.assertEquals(0, constraintViolations.size());
}
}
本文只是个入门,对数据校验有一个感性的认识,实际项目中,我们并不会像本例这样使用Validator接口的来手动校验。
介绍JSR380-BeanValidation2.0的基本用法,演示如何通过Hibernate Validator实现Java对象的属性校验,提高代码质量和维护效率。

1459

被折叠的 条评论
为什么被折叠?



