1. 概述
本文将带你快速掌握 使用标准 JSR-380 框架对 Java Bean 进行校验 的核心知识点。该规范对应的是 Jakarta Bean Validation 3.0,是在 Java EE 7 引入的 Bean Validation API 基础上的演进版本。
在绝大多数应用中,用户输入校验是一个高频刚需。而 Java Bean Validation 框架早已成为处理这类逻辑的事实标准,无论是 Spring Boot 还是 Jakarta EE 项目,都深度集成了这套机制。
2. JSR 380 规范简介
JSR 380 是 Java Bean 校验 API 的官方规范,属于 Jakarta EE 和 Java SE 的一部分。它通过注解(如 @NotNull
、@Min
、@Max
)来确保 Bean 的字段满足特定条件。
⚠️ 注意:该版本要求 JDK 17 或更高版本,因为它依赖 Spring Boot 3.x,而后者引入了 Hibernate Validator 8.0.0。同时,它也支持 Java 9+ 的新特性,比如 Stream 增强、Optional 优化、模块化系统以及接口中的私有方法等。
想深入了解规范细节?可查阅官方文档:JSR 380
3. 依赖配置
在最新的 spring-boot-starter-validation
中,会自动引入 hibernate-validator
作为传递依赖。
如果你只想手动添加校验依赖,可以直接在 pom.xml
中声明:
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
✅ 小贴士:hibernate-validator
和 Hibernate ORM 完全不是一回事。引入它并不会把 Hibernate 的持久层功能带进项目,别被名字误导了。
4. 使用校验注解
我们以一个 User
类为例,演示如何添加基本校验规则:
public class User {
@NotNull(message = "Name cannot be null")
private String name;
@AssertTrue(message = "Working must be true")
private boolean working;
@Size(min = 10, max = 200, message
= "About Me must be between 10 and 200 characters")
private String aboutMe;
@Min(value = 18, message = "Age should not be less than 18")
@Max(value = 150, message = "Age should not be greater than 150")
private int age;
@Email(message = "Email should be valid")
private String email;
// standard setters and getters
}
上述注解均来自 jakarta.validation.constraints
包,是 JSR 标准的一部分,用途如下:
@NotNull
:确保字段非 null@AssertTrue
:布尔值必须为 true@Size
:适用于 String、Collection、Map、数组,限制元素个数或字符串长度@Min
/@Max
:数值类型,限定最小/最大值@Email
:邮箱格式校验(简单正则,别指望太严格)
常用扩展注解一览
注解 | 说明 |
---|---|
@NotEmpty |
非 null 且非空(集合、字符串等) |
@NotBlank |
仅用于文本,非 null 且去空格后非空 |
@Positive / @PositiveOrZero |
正数 / 非负数 |
@Negative / @NegativeOrZero |
负数 / 非正数 |
@Past / @PastOrPresent |
日期在过去 / 过去或当前 |
@Future / @FutureOrPresent |
日期在未来 / 未来或当前 |
高级用法:支持泛型和 Optional
✅ 集合元素校验:可以直接标注在泛型上
List<@NotBlank String> preferences;
这样每次往 preferences
添加字符串时,都会自动触发 @NotBlank
校验。
✅ Optional 支持:JSR 380 支持 Java 8 的 Optional
,框架会自动解包并校验内部值
private LocalDate dateOfBirth;
public Optional<@Past LocalDate> getDateOfBirth() {
return Optional.of(dateOfBirth);
}
框架会自动提取 LocalDate
并验证是否为过去时间。
5. 编程式校验
虽然 Spring 等框架可以通过 @Valid
注解自动触发校验,但在一些非 Web 场景或测试中,我们可能需要手动调用校验 API。
手动初始化 Validator
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
拿到 Validator
实例后,就可以对任意 Bean 进行校验。
5.1 构造一个非法用户对象
User user = new User();
user.setWorking(true);
user.setAboutMe("Its all about me!");
user.setAge(50);
// name 为 null,违反 @NotNull
5.2 执行校验并处理结果
Set<ConstraintViolation<User>> violations = validator.validate(user);
校验结果是一个 ConstraintViolation<User>
的集合。遍历即可获取所有错误信息:
for (ConstraintViolation<User> violation : violations) {
log.error(violation.getMessage());
}
在这个例子中,输出会是:
Name cannot be null
简单粗暴,一目了然。
6. 校验注解的单元测试
掌握编程式校验后,写单元测试就非常方便了。
初始化 Validator
private Validator validator;
@BeforeEach
void setUp() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
测试合法对象
User user = new User();
user.setName("John Doe");
user.setWorking(true);
user.setAboutMe("I love coding and hiking.");
user.setAge(28);
user.setEmail("john.doe@example.com");
Set<ConstraintViolation<User>> violations = validator.validate(user);
assertTrue(violations.isEmpty()); // ✅ 应无错误
测试非法对象(踩坑重点)
User user = new User();
user.setName(null);
user.setWorking(false);
user.setAboutMe("hi"); // 太短
user.setAge(10); // 太小
user.setEmail("not-an-email");
Set<ConstraintViolation<User>> violations = validator.validate(user);
assertFalse(violations.isEmpty());
assertThat(violations).hasSize(5);
assertThat(violations).extracting(ConstraintViolation::getMessage)
.containsExactlyInAnyOrder(
"Name cannot be null",
"Working must be true",
"About Me must be between 10 and 200 characters",
"Age should not be less than 18",
"Email should be valid");
通过正反用例覆盖,确保你的校验逻辑万无一失。
7. 总结
本文系统梳理了 Jakarta Bean Validation 3.0(JSR-380)的核心用法,涵盖:
- 常用注解及其适用场景
- 集合与 Optional 的高级支持
- 编程式校验 API 的使用
- 单元测试的最佳实践
这些内容是构建健壮后端服务的基础能力,建议集合并在项目中实践。所有示例代码均可在 GitHub 获取: