参数校验:@Valid 与分组校验
结论先行:Spring Boot 参数校验采用 Bean Validation 规范(JSR-380):实现为 hibernate-validator,注解来自 jakarta.validation。Controller 入参上标 @Valid(或 @Validated)触发校验,失败抛 MethodArgumentNotValidException(@RequestBody 场景),由全局异常处理器统一转 400 + 字段错误。核心差异一句话:@Valid 是 JSR 标准注解、负责触发与级联校验;@Validated 是 Spring 的,额外支持分组(groups)与类/方法级校验。分组校验 = @Validated(某组.class) 只校验该组注解。
一、@Valid vs @Validated
| 对比项 | @Valid | @Validated |
|---|
| 来源 | jakarta.validation(JSR-380) | Spring(org.springframework.validation.annotation) |
| 分组校验 | 不支持(固定 Default 组) | 支持,groups 指定激活组 |
| 级联(内嵌对象) | 支持(对象字段上标 @Valid) | 支持 |
| 方法参数/返回值校验 | 需配合 Spring 机制 | 类上标注即启用 |
| Controller 入参触发 | 可以 | 可以(更推荐,语义统一) |
二、代码示例(分组校验)
public class UserDTO {
public interface Create { } // 组 1:新增
public interface Update { } // 组 2:修改
@NotBlank(groups = Create.class, message = "姓名必填") // 仅新增时校验
private String name;
@NotNull(groups = Update.class) // 仅修改时校验
private Long id;
@Min(value = 1, groups = {Create.class, Update.class})
private Integer age;
@Pattern(regexp = "^1\\d{10}$", message = "手机号格式不正确")
private String phone; // 未指定 groups → 属于 Default 组
}
@PostMapping("/users") // 只校验 Create 组
public Result<Void> create(@Validated(Create.class) @RequestBody UserDTO dto) { ... }
@PutMapping("/users/{id}") // 只校验 Update 组
public Result<Void> update(@Validated(Update.class) @RequestBody UserDTO dto) { ... }
三、常用校验注解速记
| 注解 | 作用 |
|---|
| @NotNull | 值非 null |
| @NotBlank / @NotEmpty | 字符串非空白 / 集合字符串非空 |
| @Size(min,max) | 长度/大小范围 |
| @Min / @Max / @DecimalMin / @Positive | 数值范围 |
| @Pattern | 正则 |
| @Email | 邮箱格式 |
| @Valid(级联) | 校验内嵌对象 |
常见追问 / 记忆点
- 追问 1:校验失败的异常类型?答:@RequestBody 校验失败抛 MethodArgumentNotValidException;@RequestParam/@PathVariable 等单参数校验失败抛 ConstraintViolationException(需在全局异常里分别处理)。
- 追问 2:不指定组时校验哪些?答:默认 Default 组——即未声明 groups 的注解。
- 追问 3:Service 层方法参数也能校验吗?答:能,@Validated 标在类上 + 参数/返回值加约束注解即可(Spring 方法级校验)。
- 记忆点:"@Valid 只管触发与级联、@Validated 才管分组;校验注解标字段,groups 按场景分组激活"。