摘要
@Valid 是 Bean Validation 的标准注解,@Validated 是 Spring 封装,多了分组校验和方法级校验,嵌套级联却得靠 @Valid。讲清两者区别与校验流程,并拆解嵌套漏加 @Valid、分组吃掉默认组、内部调用不走代理三个常见坑。
先说结论
@Valid 是 Bean Validation(JSR-380)的标准注解,@Validated 是 Spring 自己包的一层。后者多出来的能力就两样:分组校验和方法级校验;前者的强项是嵌套级联校验。
日常在 Controller 上做参数校验,两者大部分时候能互换,但一到嵌套对象或分组,行为就分岔了。
为什么会有两个
Bean Validation 规范定义了一套注解(@NotNull、@Size 这些),@Valid 就是规范里的「触发器」,告诉实现(Hibernate Validator)该校验这个对象了。它本身不认识 Spring,也不支持分组,只能整包校验。
Spring 想加两个能力:一是按场景只校验一部分字段(新增、更新字段要求不一样),二是让普通 Bean 的方法参数也能校验。于是搞了 @Validated,底层还是同一套 Validator,只是多了入口。
// 分组:新增时不校验 id,更新时必须校验
public interface Create {}
public interface Update {}
public class UserDTO {
@Null(groups = Create.class)
@NotNull(groups = Update.class)
private Long id;
@NotBlank
private String name;
}
@PostMapping("/user")
public void create(@Validated(Create.class) @RequestBody UserDTO dto) { }
@PutMapping("/user")
public void update(@Validated(Update.class) @RequestBody UserDTO dto) { }
校验流程长啥样
这里注意「嵌套对象级联」这一步:想让内层对象的约束生效,字段上必须挂 @Valid(或 @Validated 的话配 @Valid)。这是规范的规矩,不是 Spring 的。
public class OrderDTO {
@NotNull
private Long userId;
@Valid // 少了这行,address 里的约束一个都不跑
@NotNull
private AddressDTO address;
}
差异对照
| 维度 | @Valid | @Validated |
|---|---|---|
| 出身 | Bean Validation 规范 | Spring 自己的 |
| 分组校验 | 不支持 | 支持 groups |
| 嵌套级联 | 支持 | 需配合 @Valid |
| 方法参数校验 | 不支持 | 放类上 + AOP 生效 |
| 校验出错 | 抛 BindException/MethodArgumentNotValidException | 方法级抛 ConstraintViolationException |
方法级校验:@Validated 的独门能力
给 Service 方法参数加约束,得在类上标 @Validated,让 Spring 生成代理:
@Service
@Validated
public class UserService {
public UserDTO get(@Min(1) Long id) {
return repo.findById(id);
}
}
调用 userService.get(0L) 会直接抛 ConstraintViolationException。这套靠 AOP 代理实现,所以坑也在这儿。
坑,三个
- 嵌套忘了
@Valid:内层对象静默通过,什么错都不报,上线才发现脏数据。嵌套一层还行,嵌套 List 里的元素也要在 List 字段上标。 - 分组吃掉默认组:一旦写
@Validated(Create.class),那些没标groups的约束(默认属Default组)就不校验了。想两头都跑,得@Validated({Default.class, Create.class})。 - 内部调用不走代理:
UserService里自己this.get(0),没经过代理,校验直接跳过。方法级校验别指望它做安全兜底。
小结
Controller 参数校验用 @Validated(顺手支持分组),嵌套对象字段上老老实实加 @Valid;Service 方法级校验只在类上挂 @Validated 的场景用。别把 @Validated 当 @Valid 的升级版无脑替换,嵌套那块它不顶用。
已是最后一篇
全部评论(0)
还没有任何评论哟~