Spring Boot 校验分组(Validation Groups)高级用法全指南
Spring Boot 校验分组(Validation Groups)高级用法全指南
版本:Spring Boot 3.x + Jakarta Bean Validation 3.x
目标:用 同一份 DTO 支撑 “新增 / 修改 / 导入 / 审核” 等多套校验规则,零冗余、零硬编码、易扩展。
一、为什么需要校验分组?
场景 | 规则差异示例 |
---|---|
新增 | id 必须为 null |
修改 | id 必须非 null |
导入 | 批量字段可空,但格式必须合法 |
审核 | 只校验备注长度,其余忽略 |
传统写法:为每个场景写独立 DTO → 类爆炸、重复字段、维护困难。
分组写法:一个 DTO + N 个场景接口 → 按需启用,极致简洁。
二、核心三要素(牢记即可)
要素 | 作用 | 示例 |
---|---|---|
分组接口(Marker Interface) | 标记场景 | interface Create {} |
分组注解 | 指定规则生效范围 | @NotNull(groups = Update.class) |
分组校验 | 调用时声明场景 | @Validated(Create.class) |
三、快速上手(3 步走)
1️⃣ 定义分组接口
public interface Create {} // 新增
public interface Update {} // 修改
public interface Import {} // 批量导入
public interface Audit {} // 审核
2️⃣ 在 DTO 上按场景声明规则
@Data
public class UserDTO {// 新增时 id 必须为 null;修改时 id 必须非 null@Null(groups = Create.class)@NotNull(groups = Update.class)private Long id;// 所有场景必填@NotBlank(groups = {Create.class, Update.class, Import.class})private String username;// 仅导入场景校验邮箱格式@Email(groups = Import.class)private String email;// 仅审核场景校验备注长度@Size(max = 500, groups = Audit.class)private String remark;
}
3️⃣ 在 Controller 指定场景
@RestController
@RequestMapping("/user")
@Validated // 告诉 Spring 启用方法级校验
public class UserController {@PostMappingpublic String create(@Validated(Create.class) @RequestBody UserDTO dto) {return "新增成功";}@PutMapping("/{id}")public String update(@PathVariable Long id,@Validated(Update.class) @RequestBody UserDTO dto) {dto.setId(id);return "修改成功";}@PostMapping("/batch")public String batch(@Validated(Import.class) @RequestBody List<@Valid UserDTO> list) {return "导入成功";}@PostMapping("/{id}/audit")public String audit(@PathVariable Long id,@Validated(Audit.class) @RequestBody UserDTO dto) {dto.setId(id);return "审核成功";}
}
四、高级技巧(生产常用)
1️⃣ 组继承(减少重复注解)
public interface Create extends Default {} // 继承默认组
public interface Update extends Default {}
默认组
javax.validation.groups.Default
可省略groups = Default.class
。
2️⃣ 分组序列(按顺序校验)
@GroupSequence({Create.class, SecondStep.class})
public interface CreateTwoStep {}
- 先校验
Create
规则 → 通过后继续SecondStep
,避免一次性抛出所有错误。
3️⃣ 动态分组(运行时决定)
@Component
public class ValidationGroupSelector {public Class<?> resolveGroup(String operation) {return switch (operation) {case "create" -> Create.class;case "update" -> Update.class;case "import" -> Import.class;default -> Default.class;};}
}
在 Service 层使用:
Set<ConstraintViolation<UserDTO>> violations =validator.validate(dto, groupSelector.resolveGroup(operation));
五、与常见框架集成
框架 | 用法 |
---|---|
Spring MVC | @Validated(Group) + 全局异常处理 |
Spring Batch | ItemProcessor 中手动 validator.validate(item, Import.class) |
MapStruct | 映射后调用 validator.validate(dto, Update.class) 确保 DTO 合法 |
六、避坑清单
坑 | 解决 |
---|---|
默认组未生效 | 显式继承 Default 或显式声明 groups = Default.class |
集合参数未生效 | 使用 List<@Valid DTO> + @Validated(Group) |
构造器注入 | 构造器参数上同样加 @Validated(Group) |
七、一句话总结
“一个 DTO + N 个 Marker 接口”
让 Spring Boot 的 同一份数据模型 在 不同业务场景 下拥有 不同校验规则,
彻底告别 DTO 冗余 与 if-else 校验地狱!