Skip to content

Spring 中的 @Validated 和 @Valid 注解有什么区别? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

两者都触发 Bean Validation 校验,区别在来源和粒度:

  • @Valid:JSR/Jakarta 标准注解,来自 jakarta.validation 包。支持嵌套校验(对象里套对象,字段上标 @Valid 级联下钻),但只能标在参数、字段、方法上;
  • @Validated:Spring 提供,在标准能力上增加分组校验(同一 DTO 在"创建"和"更新"场景校不同规则)和方法级校验(直接标在 Service 类/方法上,参数约束生效)。
java
// Controller 入口用 @Validated/@Valid 开启 DTO 校验
public Result create(@Validated(Create.class) @RequestBody OrderDTO dto) {}
// 嵌套对象在字段上补 @Valid 级联

业务架构师视角,参数校验是业务规则的第一道防线,我的治理要求:

  1. 入参校验在接入层完成:DTO 上 @NotNull/@Size/@Pattern + 分组,脏数据进门即拒(400),不消耗下游服务与数据库资源——大促时这直接是容量问题;
  2. 校验规则与业务语义对齐:手机号格式、金额区间这些写在 DTO 注解上,比散在 if 里更可审计;但跨字段的业务规则(库存>0 且 用户等级达标)属于领域逻辑,放 Service,不要硬塞进校验注解——校验框架管"形状",领域层管"规则";
  3. 校验失败统一异常翻译成标准错误响应,前端能按字段定位提示。

🏗 系统架构师 ​

  • @Valid(JSR 标准注解):标在参数/字段上做级联校验——对象内嵌套对象递归生效;
  • @Validated(Spring 扩展):标在类上开启方法级参数校验(AOP 拦截),并支持分组校验——同一对象按场景应用不同规则(Create 组与 Update 组字段要求不同)。

典型组合:Controller 入参 @Valid 触发嵌套校验;Service 方法 @Validated 加参数约束兜底内部调用。

架构视角——校验放哪一层:

  1. 边界 fail-fast:外部输入在 API 入口校验完,脏数据不进系统;MethodArgumentNotValidException 统一转 400 契约;
  2. 不在每层重复深校验:入口校验格式与约束,领域层校验业务不变量(金额大于零且不超余额),两者语义不同;
  3. 方法级校验本质是 AOP,注意自调用失效;
  4. 校验规则沉淀为平台约束注解(手机号、身份证),一致性靠复用而不是每个开发者各写一份。

💻 全栈开发专家 ​

核心差异对比:

维度@Valid@Validated
来源Jakarta Bean Validation 标准Spring 提供
分组校验不支持支持(group)
嵌套校验支持(字段上再标触发递归)不支持
常用位置Controller 入参类/方法/参数(Service 层校验)

典型组合:

java
@PostMapping
public Result create(@RequestBody @Valid OrderReq req) { ... }
// 失败抛 MethodArgumentNotValidException → 400

@Service
@Validated
public class OrderService {
    public void cancel(@NotNull @Min(1) Long orderId) { ... }
}
// 失败抛 ConstraintViolationException

两个必踩坑:

  1. 两种异常类型不同,全局异常处理器必须分开接,漏一个前端就收到 500 而不是 400
  2. 嵌套对象必须在内层字段加 @Valid(如 @Valid private List<ItemReq> items),漏标后内层规则静默不生效——"校验配了但不生效"九成是这个

@Validated 的分组能力:定义 Create/Update 组,同一 POJO 不同接口校验不同字段集(更新时 id 必填、创建时必空),这是它存在的核心价值。