好的,我们来全面、系统、深入 地梳理 Java 生态(尤其是 Spring Boot)中数据校验(Data Validation)的完整机制。内容涵盖:
- 默认校验依赖(JAR 包)
- 标准规范演进(JSR-303 → JSR-380 → Jakarta Bean Validation)
- 所有内置校验注解(分门别类,含说明与示例)
- 校验分组(Groups)
- 级联校验(@Valid on nested objects)
- 自定义校验注解开发
- 与 Spring Boot 的集成方式
- 异常处理机制
- 正则表达式在校验中的核心作用
- 与其他校验工具对比(如 Guava Preconditions、Apache Commons Validator)
一、默认校验 JAR 包(Spring Boot 环境)
Spring Boot 3.x(基于 Jakarta EE 9+)默认使用:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
该 starter 会自动引入:
jakarta.validation:jakarta.validation-api→ 规范接口org.hibernate.validator:hibernate-validator→ 官方参考实现org.glassfish:jakarta.el→ 支持错误消息中的表达式(如${validatedValue})
Spring Boot 2.x 使用的是
javax.validation,3.x 起包名改为jakarta.validation。
二、规范演进
| 规范 | JSR | 版本 | 主要特性 |
|---|---|---|---|
| Bean Validation 1.0 | JSR-303 | 2009 | 基础注解(@NotNull, @Min 等) |
| Bean Validation 2.0 | JSR-380 | 2017 | 支持容器元素校验(List<@Email String>)、Java 8 时间类型、自定义注解增强 |
| Jakarta Bean Validation 3.0+ | - | 2020+ | 包名从 javax → jakarta,兼容 Jakarta EE 9+ |
三、所有内置校验注解(按类别)
1. 空值校验
| 注解 | 适用类型 | 说明 |
|---|---|---|
@Null |
任意 | 必须为 null |
@NotNull |
任意 | 不能为 null(但可为空字符串) |
@NotEmpty |
String, Collection, Map, Array | 不能为 null 且 size() > 0 |
@NotBlank |
String | 不能为 null 且 trim() 后长度 > 0 |
2. 数值校验
| 注解 | 说明 |
|---|---|
@Min(value) |
≥ value(支持 long、int、BigDecimal 等) |
@Max(value) |
≤ value |
@DecimalMin(value) |
字符串或数值 ≥ value(可指定 inclusive) |
@DecimalMax(value) |
字符串或数值 ≤ value |
@Positive |
> 0(JSR-380) |
@PositiveOrZero |
≥ 0 |
@Negative |
< 0 |
@NegativeOrZero |
≤ 0 |
3. 长度与大小校验
| 注解 | 适用类型 | 说明 |
|---|---|---|
@Size(min, max) |
String, Collection, Map, Array | 元素个数或字符串长度在 [min, max] |
@Digits(integer, fraction) |
数值或字符串 | 整数部分最多 integer 位,小数部分最多 fraction 位(用于金额校验) |
4. 格式校验(含正则)
| 注解 | 说明 |
|---|---|
@Email |
邮箱格式(底层用正则) |
@Pattern(regexp, flags) |
自定义正则表达式校验(核心!) |
@URL |
URL 格式(可指定协议、主机等) |
5. 时间校验
| 注解 | 说明 |
|---|---|
@Past |
时间在当前时间之前 |
@PastOrPresent |
≤ 当前时间 |
@Future |
> 当前时间 |
@FutureOrPresent |
≥ 当前时间 |
支持
java.util.Date,java.time.*(LocalDateTime, ZonedDateTime 等)
6. 布尔校验
| 注解 | 说明 |
|---|---|
@AssertTrue |
boolean/Boolean 必须为 true |
@AssertFalse |
必须为 false |
7. 容器元素校验(JSR-380 新增)
java
public class User {
// 校验 List 中的每个 email
private List<@Email String> emails;
// 校验 Map 的 value
private Map<String, @NotBlank String> metadata;
}
四、校验分组(Groups)
用于不同场景使用不同校验规则,例如:
- 新增时:
id可为空 - 更新时:
id必须存在
java
public interface CreateGroup {}
public interface UpdateGroup {}
public class UserDTO {
@NotNull(groups = UpdateGroup.class)
private Long id;
@NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
private String name;
}
// Controller
@PostMapping("/create")
public void create(@Validated(CreateGroup.class) @RequestBody UserDTO user) { }
@PutMapping("/update")
public void update(@Validated(UpdateGroup.class) @RequestBody UserDTO user) { }
五、级联校验(@Valid)
校验嵌套对象:
java
public class Order {
@Valid
private User buyer; // 会递归校验 User 内部的字段
}
public class User {
@NotBlank
private String name;
}
六、自定义校验注解
步骤:
- 定义注解
- 实现
ConstraintValidator
java
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
public @interface IdCard {
String message() default "身份证格式错误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
@Override
public boolean isValid(String value, ConstraintValidationContext context) {
if (value == null) return true;
return value.matches("^\\d{17}[\\dXx]$"); // 简化版
}
}
七、Spring Boot 集成方式
- Controller 参数校验 :
@Valid/@Validated - Service 层校验 :手动调用
Validator - 配置文件启用/禁用 :
spring.jpa.properties.javax.persistence.validation.mode=none(如需关闭 JPA 自动校验)
八、异常处理
校验失败时抛出:
MethodArgumentNotValidException(Controller 参数校验失败)ConstraintViolationException(手动调用 Validator 失败)
建议全局异常处理:
java
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}
九、正则表达式与校验的关系(核心)
@Pattern(regexp = "...")是唯一允许你写任意正则的内置注解。- 正则表达式用于精确控制字符串格式 ,例如:
- 手机号:
^1[3-9]\d{9}$ - 身份证:
^\d{17}[\dXx]$ - 强密码:
^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{8,}$
- 手机号:
注意:正则性能、安全性(避免 ReDoS)需谨慎。
十、与其他校验工具对比
| 工具 | 定位 | 特点 |
|---|---|---|
| Jakarta Bean Validation | 标准、声明式、与框架集成 | 注解驱动,支持分组、级联、国际化 |
| Guava Preconditions | 运行时参数校验 | 用于方法内部 checkNotNull(),不用于 DTO 校验 |
| Apache Commons Validator | 传统工具类 | 手动调用 EmailValidator.getInstance().isValid(email),无注解支持 |
| Spring Assert | Spring 内部工具 | Assert.hasText(),常用于私有方法参数校验 |
✅ 结论 :在 Spring Boot Web 项目中,Jakarta Bean Validation 是首选,其他工具用于特定场景。
总结图谱
数据校验体系
├── 规范:Jakarta Bean Validation (JSR-380+)
├── 实现:Hibernate Validator(默认)
├── 校验方式
│ ├── 内置注解(40+种,覆盖空值、数值、格式、时间等)
│ ├── 正则表达式(@Pattern 核心)
│ ├── 自定义注解(ConstraintValidator)
│ ├── 分组校验(Create/Update 场景)
│ └── 级联校验(@Valid 嵌套对象)
├── 框架集成:Spring Boot(@Valid + 全局异常)
└── 补充工具:Guava / Commons(非主流 Web 校验)
如果你需要 完整可运行的示例项目(含分组、自定义注解、正则、异常处理),我可以为你生成。是否需要?