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 获取:

👉 https://github.com/eugenp/tutorials/tree/master/javaxval


原始标题:Java Bean Validation Basics | Baeldung