💡 一则也许对你有用的小广告 🏆

欢迎飞飞程序员   ,你将获得:专属的实战项目(已更新的所有会员标识的项目都能学习) / 1v1 提问 / Java 学习路线 / PHP 学习路线 / 学习打卡 / 社群讨论

  • 正在进行中的项目:《FFBlog知识付费博客项目》 正在持续更新中,基于 Spring Boot 3.x + JDK 21...,点击查看 ;
  • 《从零开发:FFBlog知识付费博客项目(全栈开发)》 演示链接: https://ffblog.ffcxy.com/  ;

截止目前, 飞飞  正在疯狂爆肝实战项目,后续还会上新更多项目,目标是将所学知识开发成项目并且分享给大家,如知识付费系统, Ai系统, CMS系统,在线商城系统,等等 ,欢迎点击围观

摘要
@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 代理实现,所以坑也在这儿。

坑,三个

  1. 嵌套忘了 @Valid:内层对象静默通过,什么错都不报,上线才发现脏数据。嵌套一层还行,嵌套 List 里的元素也要在 List 字段上标。
  2. 分组吃掉默认组:一旦写 @Validated(Create.class),那些没标 groups 的约束(默认属 Default 组)就不校验了。想两头都跑,得 @Validated({Default.class, Create.class})。
  3. 内部调用不走代理:UserService 里自己 this.get(0),没经过代理,校验直接跳过。方法级校验别指望它做安全兜底。

小结

Controller 参数校验用 @Validated(顺手支持分组),嵌套对象字段上老老实实加 @Valid;Service 方法级校验只在类上挂 @Validated 的场景用。别把 @Validated 当 @Valid 的升级版无脑替换,嵌套那块它不顶用。

上一篇 JSR380 参数校验的基础用法
已是最后一篇

全部评论(0)

头像
😃 😁 😅 😂 😍 😜 😝 🤑 🥵 🥰 😙 😎 😵 😭 😱 😖 🥳 👽 🙈 🤡 😤 💣 💯 💢 ❤️ 👍 👏 👋 👌 🤏 🙏
还没有任何评论哟~
微信扫一扫
微信二维码
微信客服
QQ 咨询
QQ二维码
工作日 9:00 - 18:00 在线
QQ客服
在线工单
提交问题工单,我们会尽快回复处理。
提交工单
在线工单