Spring Boot分组校验是解决DTO多场景复用冲突的核心手段,通过定义空接口标记分组(如Create、Update),在DTO字段注解中指定groups,在Controller方法参数用@Validated(XXX.class)启用对应校验。

Spring Boot 的分组校验不是“额外功能”,而是解决实际开发中同一 DTO 多场景复用冲突的核心手段。它让新增、修改、导入、审核等不同操作,共用一个实体类,却各自执行专属校验规则。
定义分组接口
分组本质是一组空接口(Marker Interface),只起标记作用,不写方法。通常按业务动作命名:
- public interface Create {} —— 用于新增场景
- public interface Update {} —— 用于更新场景
- public interface Import {} —— 用于批量导入
- public interface Audit {} —— 用于审核流程
注意:不要实现任何逻辑,也不需要加注解;接口名大小写和包路径保持清晰一致即可。
在 DTO 中按组挂约束
字段上的校验注解通过 groups 属性绑定到特定分组。未指定 groups 的注解,默认属于 Default 组,会在所有带 @Validated(无参数)或显式含 Default.class 的校验中生效。
示例:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
@Data
public class UserDTO {
@Null(groups = Create.class) // 新增时必须为空
@NotNull(groups = Update.class) // 更新时必须非空
private Long id;
@NotBlank(groups = Create.class) // 新增必填
@Size(min = 2, max = 20, groups = {Create.class, Update.class})
private String username;
@Email(groups = Import.class) // 仅导入时校验邮箱格式
private String email;
@Size(max = 500, groups = Audit.class) // 仅审核时校验备注长度
private String remark;
}
关键点:
• 同一字段可同时挂多个分组注解,互不干扰
• 不写 groups 的约束(如 @NotBlank 单独出现)= 全局生效,慎用
Controller 中指定校验组
在接口方法参数上,用 @Validated(XXX.class) 明确启用哪一组规则:
@PostMapping("/user")<br> public R create(@Validated(Create.class) @RequestBody UserDTO dto)@PutMapping("/user/{id}")<br> public R update(@PathVariable Long id, @Validated(Update.class) @RequestBody UserDTO dto)@PostMapping("/import")<br> public R importBatch(@Validated(Import.class) @RequestBody List<@Valid UserDTO> list)
注意:
• Controller 类上加 @Validated(无参)无效,必须写在具体方法参数上
• 集合批量校验时,需配合 @Valid 对泛型元素启用嵌套校验
配套建议:全局异常统一处理
校验失败会抛出 MethodArgumentNotValidException。推荐配置全局异常处理器,提取错误信息并返回结构化响应:
@RestControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidation(MethodArgumentNotValidException ex) {
String errorMsg = ex.getBindingResult()
.getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return Result.fail(errorMsg);
}
}
这样前端收到的始终是统一格式的提示,比如:{"code":400,"msg":"ID不能为空; 用户名不能为空"}

















