侧边栏壁纸
博主头像
sirgo的博客 博主等级

每天进步一点点,一年之后你会看到巨大的变化

  • 累计撰写 58 篇文章
  • 累计创建 46 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Spring @Valid和@Validator备忘

sirgo
2025-10-27 / 0 评论 / 0 点赞 / 67 阅读 / 0 字

@Valid

@Valid 用于对标记的成员属性进行验证。该注解不支持分组验证。它Bean Validation API(JSR-303 / JSR-380) 的标准注解,属于 Java EE / Jakarta EE 规范不是 Spring 特有

  • 包路径:javax.validation.Valid(Java EE)或 jakarta.validation.Valid(Jakarta EE 9+)

  • 作用:标记一个对象需要被递归验证(包括其嵌套属性)

它本身不执行验证,只是“告诉验证器:请验证这个对象”。 因此在(非Spring)项目中需要手动进行调用方法手动进行校验。但Spring 对 Bean Validation 做了深度集成在特定场景下自动触发验证

使用方法

  1. 依赖(以 Hibernate Validator 为例)

    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>8.0.1.Final</version> <!-- Jakarta EE 9+ -->
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.el</artifactId>
        <version>4.0.2</version>
    </dependency>
  2. 定义带约束的实体

    import jakarta.validation.constraints.NotBlank;
    import jakarta.validation.constraints.Size;
    
    public class User {
        @NotBlank
        @Size(min = 2, max = 20)
        private String name;
    
        // getter/setter
    }
  3. 手动触发验证

    import jakarta.validation.Validation;
    import jakarta.validation.Validator;
    import jakarta.validation.ValidatorFactory;
    import jakarta.validation.ConstraintViolation;
    import java.util.Set;
    
    public class Main {
        public static void main(String[] args) {
            // 1. 创建 Validator
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            Validator validator = factory.getValidator();
    
            // 2. 构造对象
            User user = new User();
            user.setName(""); // 违反 @NotBlank
    
            // 3. 手动验证
            Set<ConstraintViolation<User>> violations = validator.validate(user);
    
            // 4. 处理结果
            if (!violations.isEmpty()) {
                violations.forEach(v -> 
                    System.out.println(v.getPropertyPath() + ": " + v.getMessage())
                );
            }
        }
    }

在Spring中的使用方法

由于Spring对其进行了集成。因此在一些场景下会自动进行校验:

  • Controller 中的@RequestBody注解的标记的对象。

  • Controller 中 @ModelAttribute(表单提交)注解的标记的对象。

  • 嵌套对象验证(字段级)

  • Service 层方法参数(需配合 @Validated

总结

使用位置

作用

是否自动触发

依赖条件

方法参数(Controller)

验证传入对象

✅ 是

Spring MVC + @RequestBody/@ModelAttribute

方法参数(Service)

验证传入对象

✅ 是(需 @Validated)

类上有 @Validated

字段/属性

触发嵌套对象验证

✅ 是(当父对象被验证时)

父对象参与验证流程

构造器参数

验证构造时传入值

❌ 否(需手动)

普通 Java 或手动调用

普通 Java 对象

标记需验证

❌ 否

必须手动调用 Validator

使用位置

作用

是否自动触发

依赖条件

方法参数(Controller)

验证传入对象

✅ 是

Spring MVC + @RequestBody/@ModelAttribute

方法参数(Service)

验证传入对象

✅ 是(需 @Validated)

类上有 @Validated

字段/属性

触发嵌套对象验证

✅ 是(当父对象被验证时)

父对象参与验证流程

示例
场景 1:Controller 中 @RequestBody 对象
@PostMapping("/user")
public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
    // 如果 user 无效,Spring MVC 会抛出 MethodArgumentNotValidException
    return ResponseEntity.ok(userService.save(user));
}
  • 触发时机:参数绑定完成后、进入方法体前

  • 必须加 @Valid(或 @Validated)才能触发

场景 2:Controller 中 @ModelAttribute(表单提交)
@PostMapping("/register")
public String register(@Valid User user, BindingResult result) {
    if (result.hasErrors()) {
        return "register-form";
    }
    return "success";
}
  • 触发时机:表单数据绑定到对象后

  • ✅ 需配合 BindingResult 捕获错误(否则抛异常)

场景 3:嵌套对象验证(字段级)
public class Order {
    @Valid // 👈 关键!触发 Address 的验证
    private Address shippingAddress;
}

public class Address {
    @NotBlank
    private String street;
}

触发时机:当父对象被验证时,自动递归验证带 @Valid 的字段

场景 4:Service 层方法参数(需配合 @Validated
@Service
@Validated // 👈 启用 AOP 验证
public class UserService {
    public void update(@Valid User user) {
        // 若 user 无效,抛 ConstraintViolationException
    }
}
  • 触发时机:方法被调用时(通过 Spring AOP 代理拦截)

  • 必须类上有 @Validated,否则不生效

最后

  1. @Valid 本身不执行验证
    它只是一个“标记”,真正的验证由 Validator 实现(如 Hibernate Validator) 执行。

  2. Spring 中的自动触发是有条件的

    • Controller:靠 Spring MVC 框架

    • Service:靠 Spring AOP(需 @Validated

  3. 嵌套验证必须加 @Valid
    否则子对象的约束不会被检查。

  4. 异常类型不同

    • Controller(@RequestBody)→ MethodArgumentNotValidException

    • Service(AOP)→ ConstraintViolationException

    • 手动验证 → 返回 Set<ConstraintViolation>

@Validated

@ValidatedSpring Framework 提供的一个注解,用于启用方法级别(method-level)的参数验证,是对标准 Bean Validation(JSR-303/JSR-380)中 @Valid扩展和增强该注解基于Spring AOP进行拦截进行校验。

与@Valid的区别

特性

@Valid

@Validated

来源

Bean Validation 标准(JSR)

Spring 框架扩展

支持分组验证

❌ 不支持

✅ 支持(@Validated(Group.class)

可用于简单参数(如 @RequestParam

❌ 不能

✅ 可以(需类上加 @Validated

可用于 Service 层方法验证

❌ 不能(无 AOP)

✅ 可以(需类上加 @Validated

支持嵌套对象验证

✅(字段上加 @Valid

❌ 本身不处理嵌套(但可与 @Valid 配合)

是否依赖 Spring AOP

❌ 否

✅ 是

典型使用场景总结

场景

是否需要 @Validated

说明

Controller 中 @RequestBody 验证

❌ 不需要

@Valid 即可

Controller 中 @RequestParam 验证

✅ 需要(类上)

否则不生效

Service 层方法参数验证

✅ 需要(类上)

启用 AOP 验证

分组验证(多步骤表单)

✅ 需要

@Valid 无法实现

嵌套对象验证

❌ 不需要

用字段上的 @Valid

其他

@Validated也可用于方法和参数上面,但最常见在Class类上使用。 如果一个Controller只需要为一个方法开启校验则只需放在该方法上但需注意:方法上的 @Validated 不能指定分组(分组只能在参数上指定,可以在方法参数内指定分组)。

场景

推荐用法

整个 Service/Controller 都需要参数验证

类上加 @Validated

只有 1~2 个方法需要验证

方法上加 @Validated

需要分组验证

类或方法上加 @Validated + 参数上 @Validated(Group.class)

0

评论区