在 Spring Boot 应用程序中,@Validated 和 @Valid 都是用于数据校验的注解,但它们之间存在一些关键的区别。总的来说,@Validated 是 Spring 框架提供的对标准 JSR-303 @Valid 注解的增强。
主要区别概览
| 特性 | @Valid (JSR-303) |
@Validated (Spring) |
|---|---|---|
| 来源 | Java Bean Validation 规范 (JSR-303/JSR-380) 的一部分,是标准 Java EE 注解。 | Spring 框架提供的注解,作为 @Valid 的一个变体。 |
| 功能 | 提供基本的校验功能,可以递归地校验嵌套对象。 | 继承了 @Valid 的所有功能,并增加了分组校验的功能。 |
| 应用位置 | 主要用于方法参数、成员变量和构造函数参数。 | 可以用在类、方法和方法参数上。当用在类上时,可以支持对方法参数的约束校验。 |
| 分组校验 | 不支持。 | 支持。可以根据不同的场景(如创建、更新)选择性地触发一部分校验规则。 |
| 嵌套校验 | 支持。在需要校验的嵌套对象字段上添加 @Valid 注解即可。 |
同样支持嵌套校验,用法与 @Valid 相同。 |
示例
1. 来源和基本功能
-
@Valid是 Java 标准规范的一部分,任何兼容 JSR-303/JSR-380 的实现都可以识别它。 Spring Boot 默认集成了 Hibernate Validator 作为其实现。@Valid注解能够触发对其所标注对象的整个对象图进行校验。 -
@Validated是 Spring 框架特有的注解,它在@Valid的基础上进行了扩展,最核心的增强就是支持校验分组。
2. 分组校验:@Validated 的核心优势
在实际开发中,同一个数据传输对象(DTO)可能在不同的业务场景下有不同的校验规则。例如,在创建用户时,用户名和密码是必填的;而在更新用户信息时,用户ID是必填的,但密码可能不是。这种情况下,使用 @Validated 的分组功能就非常方便。
示例:使用 @Validated 进行分组校验
首先,定义两个标记接口作为校验分组:
java
public interface CreateGroup {}
public interface UpdateGroup {}
然后,在 DTO 的字段上使用 groups 属性来指定校验规则属于哪个分组:
java
public class UserDTO {
@NotNull(message = "用户ID不能为空", groups = UpdateGroup.class)
private Long id;
@NotBlank(message = "用户名不能为空", groups = {CreateGroup.class, UpdateGroup.class})
private String username;
@NotBlank(message = "密码不能为空", groups = CreateGroup.class)
private String password;
// Getters and Setters
}
最后,在 Controller 的方法上使用 @Validated 注解并指定要生效的分组:
java
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Validated(CreateGroup.class) @RequestBody UserDTO userDTO) {
// 处理创建用户的逻辑
return ResponseEntity.ok("用户创建成功");
}
@PutMapping
public ResponseEntity<String> updateUser(@Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
// 处理更新用户的逻辑
return ResponseEntity.ok("用户更新成功");
}
}
在这个例子中:
- 当调用
createUser接口时,只会校验CreateGroup分组的规则,即username和password不能为空。 - 当调用
updateUser接口时,只会校验UpdateGroup分组的规则,即id和username不能为空。
3. 嵌套校验:@Valid 的常用场景
当一个对象包含了另一个需要校验的对象时,就需要进行嵌套校验。@Valid 在这种场景下非常实用。
示例:使用 @Valid 进行嵌套校验
假设有一个订单 Order 对象,它包含一个需要校验的 User 对象:
java
public class User {
@NotBlank(message = "用户名不能为空")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
// Getters and Setters
}
public class Order {
@NotBlank(message = "订单号不能为空")
private String orderNumber;
@NotNull(message = "用户信息不能为空")
@Valid // 关键点:在这里添加@Valid注解
private User user;
// Getters and Setters
}
在 Controller 中,直接在 Order 参数前使用 @Valid 或 @Validated 即可触发嵌套校验:
java
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public ResponseEntity<String> createOrder(@Valid @RequestBody Order order) {
// 如果Order中的User信息不合法,请求将无法进入该方法
return ResponseEntity.ok("订单创建成功");
}
}
当 createOrder 方法被调用时,校验框架不仅会校验 Order 对象的 orderNumber 字段,还会因为 user 字段上的 @Valid 注解,进而校验 User 对象内部的 name 和 email 字段。
4. 应用在非 JavaBean 参数上
@Validated 还可以用在类级别,以便能够校验方法中非 JavaBean 的参数,例如路径变量 (@PathVariable) 和请求参数 (@RequestParam)。要实现这一点,需要在 Controller 类上添加 @Validated 注解。
示例:
java
@RestController
@RequestMapping("/items")
@Validated // 放在类级别
public class ItemController {
@GetMapping("/{id}")
public ResponseEntity<String> getItemById(@PathVariable("id") @Min(1) Long id) {
return ResponseEntity.ok("获取物品成功,ID: " + id);
}
}
在这个例子中,如果请求的 id 小于1,将会触发一个 ConstraintViolationException。
总结
- 如果只需要简单的校验,不涉及复杂的分组场景,使用
@Valid就足够了。 - 当需要根据不同的业务场景应用不同的校验规则时,
@Validated的分组功能是最佳选择。 - 在进行嵌套对象校验时,务必在嵌套对象的字段上使用
@Valid注解。 - 若要校验
@PathVariable或@RequestParam这类方法参数,需要在对应的类上标注@Validated注解。