数据校验之spring-boot-starter-validation,@Valid和@Validated
本文最后更新于 2025-05-29,文章内容可能已经过时。
1、spring-boot-starter-validation
spring-boot-starter-validation
是 Spring Boot 提供的一个依赖启动器,用于在项目中快速集成 Bean Validation(JSR-380) 功能。它基于 Hibernate Validator(Spring Boot 默认的验证实现),可以方便地对 Java Bean 的属性进行校验。
一、作用
spring-boot-starter-validation
主要用于:
- 对控制器方法参数进行校验(常用于 REST API 接口中)
- 验证实体类字段是否符合预期规则(如非空、长度、格式等)
二、常用注解(Validation Annotations)
注解 | 说明 |
---|---|
@NotNull | 不能为 null |
@Null | 必须为 null |
@NotBlank | 字符串不能为 null 或空白字符串(仅适用于字符串) |
@NotEmpty | 不能为 null 或空(适用于字符串、集合、数组等) |
@Size(min=, max=) | 长度或大小范围限制 |
@Min(value) / @Max(value) | 数值最小/最大值限制 |
@Email | 必须是合法邮箱地址 |
@Pattern(regexp="正则表达式") | 符合指定的正则表达式 |
三、使用步骤
1. 添加依赖
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Gradle:
implementation 'org.springframework.boot:spring-boot-starter-validation'
2. 在 DTO 或实体类上添加验证注解
public class UserDTO {
@NotBlank(message = "姓名不能为空")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄不能小于18岁")
private int age;
// getter/setter
}
3. 在 Controller 中启用验证
在 controller 方法的参数前加上 @Valid
注解,并捕获异常:
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors().toString());
}
// 处理业务逻辑
return ResponseEntity.ok("用户创建成功");
}
}
4. 全局异常处理(可选)
使用 @ControllerAdvice
统一处理验证错误:
@ControllerAdvice
public class ValidationHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ex.getBindingResult().getAllErrors().toString());
}
}
四、注意事项⚠️
@Valid
只能在 controller 层使用,不能直接用于 service。- 如果你需要嵌套对象验证,需要在内部对象字段上也加上
@Valid
。 @Validated
支持 AOP,在 service 层使用时需要配合@Validated
和类级别注解。
五、总结
功能 | 是否支持 |
---|---|
JSR 380 标准验证 | ✅ |
常用验证注解 | ✅ |
控制器参数验证 | ✅ |
自定义验证规则 | ✅ |
嵌套对象验证 | ✅ |
全局异常统一处理 | ✅ |
2、@Valid和@Validated
在 Spring 框架中,@Valid
和 @Validated
都用于数据校验(Validation),但它们的使用场景和功能有所不同。下面是它们的详细对比与使用说明:
一、基本区别
特性 | @Valid | @Validated |
---|---|---|
来源 | javax.validation.Valid | org.springframework.validation.annotation.Validated |
是否支持分组校验 | ❌ 不支持 | ✅ 支持 |
是否支持类级别验证(Class-level validation) | ❌ 不支持 | ✅ 支持 |
是否可以在非控制器层使用 | ❌ 只能在 controller 层使用 | ✅ 可以用在 service、component 等 |
是否基于 AOP 实现 | ❌ | ✅ 是 Spring AOP 的一部分 |
二、使用场景对比
1. @Valid
适用于 Controller 层参数校验,通常配合 @RequestBody
使用。
示例:
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody UserDTO userDTO, BindingResult result) {
if (result.hasErrors()) {
return ResponseEntity.badRequest().body(result.getAllErrors());
}
// 业务逻辑
}
⚠️ 注意:
@Valid
不支持嵌套对象的自动验证,除非你在嵌套对象上也加上@Valid
。
2. @Validated
适用于更广泛的场景,包括:
- Controller 层
- Service 层
- Component 或 Configuration 类
并且支持:
- 分组校验(Group Validation)
- 类级别校验(如整个类满足某种规则)
示例(Service 层校验):
@Service
@Validated
public class UserService {
public void validateUser(@Valid UserDTO userDTO) {
// 如果不符合约束会抛出异常
}
}
三、嵌套对象校验示例
假设你有如下结构:
public class UserDTO {
@NotBlank
private String name;
@Valid
private AddressDTO address;
}
public class AddressDTO {
@NotBlank
private String city;
}
在这种情况下,如果不加 @Valid
在 address
字段上,Spring 不会自动校验嵌套对象。
四、分组校验(Groups)
只有 @Validated
支持分组校验。
定义接口作为分组标记:
public interface GroupA {}
public interface GroupB {}
DTO 中指定分组:
public class UserDTO {
@NotBlank(groups = GroupA.class)
private String username;
@Min(value = 18, groups = GroupB.class)
private int age;
// getter/setter
}
使用时指定分组:
@Validated
public interface UserService {
void validateUser(@Validated(GroupA.class) UserDTO user);
}
五、全局异常处理
你可以通过统一的异常处理器来捕获验证错误:
@RestControllerAdvice
public class ValidationExceptionAdvice {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ex.getBindingResult().getAllErrors().toString());
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ex.getMessage());
}
}
六、总结对比表
功能 | @Valid | @Validated |
---|---|---|
JSR-380 标准 | ✅ | ✅ |
可用于 Controller 参数 | ✅ | ✅ |
可用于 Service 方法参数 | ❌ | ✅ |
支持分组校验 | ❌ | ✅ |
支持类级别验证 | ❌ | ✅ |
基于 AOP | ❌ | ✅ |
嵌套对象验证 | ❌(需手动加 @Valid ) | ✅(同理) |
七、推荐使用方式
场景 | 推荐注解 |
---|---|
REST API 参数校验 | @Valid |
Service/Component 方法参数校验 | @Validated |
需要分组校验 | @Validated |
需要类级别校验 | @Validated |
如果你正在开发一个复杂的项目,并且希望实现统一的数据校验机制,建议优先使用 @Validated
,它更强大、灵活,适合大型项目或模块化设计。
3、@Valid和@Validated如何结合使用?
@Valid
和 @Validated
在 Spring 应用中可以结合使用,以发挥两者的优势。@Valid
主要用于触发单个对象的校验,而 @Validated
提供了更高级的功能,比如分组校验、AOP 支持等。下面是如何结合这两者的具体方法和示例。
一. 基本概念回顾
@Valid
: 用于触发标准 JSR-303/JSR-380 规范下的数据校验。@Validated
: 是 Spring 提供的一个注解,它不仅支持@Valid
的所有功能,还增加了对校验组(groups)的支持,并且可以在类级别上使用。
二. 结合使用的场景
通常情况下,你可能会在控制器层使用 @Valid
来校验传入的数据模型,并在服务层使用 @Validated
进行更复杂的校验逻辑,如基于分组的校验。
三. 示例代码
3.1 定义校验组
首先定义一些校验组,以便我们可以根据需要启用不同的校验规则。
public interface OnCreate {}
public interface OnUpdate {}
3.2 DTO 类与校验注解
接下来,在你的 DTO 类中应用这些校验规则,并指定它们属于哪个组。
public class UserDTO {
@NotNull(groups = {OnCreate.class, OnUpdate.class})
private Long id;
@NotBlank(groups = OnCreate.class)
private String name;
// getters and setters
}
3.3 控制器层使用 @Valid
在控制器中,你可以直接使用 @Valid
或者 @Validated
加上特定的校验组来触发校验。
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@Validated(OnCreate.class) @RequestBody UserDTO userDTO) {
// 处理业务逻辑
return ResponseEntity.ok("User created successfully");
}
@PutMapping("/{id}")
public ResponseEntity<String> updateUser(@PathVariable("id") Long id,
@Validated(OnUpdate.class) @RequestBody UserDTO userDTO) {
// 处理业务逻辑
return ResponseEntity.ok("User updated successfully");
}
}
注意:在这个例子中,我们在控制器的方法参数上使用了 @Validated
而不是 @Valid
,这是因为我们希望利用其分组校验的功能。
3.4 服务层使用 @Validated
在服务层,如果需要进行额外的校验,可以将 @Validated
注解应用于服务类,并通过方法参数或类属性的方式调用校验逻辑。
@Service
@Validated
public class UserService {
public void validateUser(@Validated(OnCreate.class) UserDTO userDTO) {
// 可能会有一些额外的业务逻辑检查
}
}
四. 总结
@Valid
更适合于简单的校验场景,尤其是在控制器层接收请求时。@Validated
则提供了更多的灵活性,包括但不限于分组校验、类级别的校验以及更广泛的应用范围(如服务层)。
通过这种方式,你可以灵活地组合使用 @Valid
和 @Validated
,以满足不同的校验需求。
- 感谢你赐予我前进的力量