在 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
注解。