BeanValidation
BeanValidation(JSR-303)是JavaEE中的JavaBean数据校验规范,用于简化和标准化数据验证过程。它允许开发人员在代码中使用注解来声明验证规则,并在运行时对这些规则进行验证。如果不使用数据校验框架,我们对用户的输入需要编写大量if/else进行判断其输入是否合法,这类代码占用大量篇幅,而且不方便复用。BeanValidation提供的声明式校验则能很好的解决这个问题。
使用BeanValidation非常简单,我们为实体类中需要校验的字段标注注解,其中声明了如何校验以及相关参数,然后通过验证器校验对象中的字段即可。
引入相关依赖
BeanValidation的标准接口包含在JavaEE SDK中,如果我们的工程最终部署到JavaEE应用服务器,引入以下依赖即可。不过注意该依赖并没有包含BeanValidation的具体实现,它通常由JavaEE应用服务器提供。
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
<scope>provided</scope>
</dependency>
不过如果不使用JavaEE平台,我们也可以单独引入相关的规范和实现框架。Hibernate Validator是BeanValidation规范的一个主流实现,对于普通Java工程,我们可以单独引入这两个包来使用BeanValidation的功能。另外Hibernate Validator又依赖于JavaEE的EL规范和其实现,因此我们还需要手动引入javax.el-api和其实现框架,这里我们选择GlassFish的实现。
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.0.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
注意:hibernate-validator在规范的基础上扩展了BeanValidation,其本身也自带了一些注解可供使用,使用时要注意区分。
使用数据验证注解
使用验证框架进行验证非常简单,一般只需要两步:
- 在实体类上标注注解
- 调用验证器获得验证结果
我们直接看一个例子。
Student.java
package com.gacfox.demo.model;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
public class Student {
@NotBlank(message = "姓名不能为空")
private String name;
@Min(value = 8, message = "入学年龄不得小于8岁")
@Max(value = 15, message = "入学年龄不得大于15岁")
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Main.java
package com.gacfox.demo;
import com.gacfox.demo.model.Student;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.setName("汤姆");
student.setAge(16);
try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
Validator validator = validatorFactory.getValidator();
// 进行验证
Set<ConstraintViolation<Student>> validateResults = validator.validate(student);
// 打印验证结果
if (validateResults != null && !validateResults.isEmpty()) {
for (ConstraintViolation<Student> result : validateResults) {
System.out.println("Field: [" + result.getPropertyPath() + "] Message:[" + result.getMessage() + "]");
}
}
}
}
}
执行结果如下。
Field: [age] Message:[入学年龄不得大于15岁]
注意:如果有多个验证错误,它们会全部输出。
代码中,我们的实体类Student上标注了若干注解,其中指定了验证规则和值,以及发生错误时输出的信息,然后在main()方法中获取了验证器并进行了数据校验。实际开发中,我们可以基于验证器API进行封装,便于使用。
BeanValidation内置的验证注解
下面列表中的是一些常用的注解。
| 注解名 | 说明 |
|---|---|
| @Null | 对象,为空 |
| @NotNull | 对象,不为空 |
| @AssertTrue | 布尔,为True |
| @AssertFalse | 布尔,为False |
| @Min(value) | 数字,最小为value |
| @Max(value) | 数字,最大为value |
| @Positive | 数字,正数 |
| @PositiveOrZero | 数字,正数或0 |
| @Negative | 数字,负数 |
| @NegativeOrZero | 数字,负数或0 |
| @Pattern(value) | 字符串,正则校验 |
| 字符串,邮箱类型 | |
| @NotBlank | 字符串,不为空字符串 |
| @Size(max, min) | 字符串、数组或集合,长度范围为min到max |
| @NotEmpty | 集合,不为空 |
| @Past | 日期,过去的日期 |
| @Future | 日期,将来的日期 |
| @PastOrPresent | 日期,过去或者现在 |
| @FutureOrPresent | 日期,将来或者现在 |
自定义验证注解
自定义验证注解也十分简单,我们仿照原版注解定义即可。下面例子中,我们定义了一个验证字符串是否为CamelCase的注解。
CamelCase.java
package com.gacfox.demo.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CamelCaseValidator.class)
public @interface CamelCase {
String message() default "指定字符串不是CamelCase格式";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
CamelCaseValidator.java
package com.gacfox.demo.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CamelCaseValidator implements ConstraintValidator<CamelCase, String> {
private static final String PATTERN = "([a-z]+[A-Z]+\\w+)+";
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
return s.matches(PATTERN);
}
}
代码中,我们的自定义注解指定了一个自定义的验证器类,其中包含了真正的验证代码。