validation
概述
spring-boot-starter-validation 是 Spring Boot 官方提供的用于数据校验的启动器,它基于 Bean Validation API (JSR 380) 标准,并默认使用 Hibernate Validator 作为其实现。这个框架能让你通过声明式的注解,轻松地对控制器接收的参数、服务层的方法参数以及任何 Java Bean 进行校验,确保数据的完整性和有效性。
添加依赖
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
常用注解
| 注解 | 适用类型 | 说明 | 示例 |
|---|---|---|---|
| 基础校验 | |||
@NotNull |
任意类型 | 校验字段值不能为 null |
@NotNull(message = "ID不能为空") |
@NotEmpty |
String/Collection/Array/Map | 校验对象不能为 null 且长度/大小必须大于 0 |
@NotEmpty(message = "列表不能为空") |
@NotBlank |
String | 校验字符串不能为 null 且去首尾空格后长度大于 0 |
@NotBlank(message = "名称不能为空") |
@Null |
任意类型 | (新增) 校验字段值必须为 null |
@Null(message = "创建时ID必须为空") |
@Size |
String/Collection/Array/Map | 校验长度或大小是否在指定范围内 | @Size(min=6, max=20, message="长度需在6-20之间") |
| 数值校验 | |||
@Min / @Max |
数值类型 | 校验数值是否大于等于/小于等于指定值 | @Min(value=18, message="年龄需大于18") |
@DecimalMin / @DecimalMax |
(新增) BigDecimal/String/数值 | 类似于 Min/Max,但支持字符串值,适用于高精度数值(如金额) | @DecimalMin(value="0.01", message="金额不能小于0.01") |
@Digits |
(新增) BigDecimal/数值/String | 校验数值必须符合指定的整数位和小数位长度 | @Digits(integer=5, fraction=2, message="金额格式错误") |
@Positive |
(新增) 数值类型 | 校验数值必须为正数(> 0) | @Positive(message = "数量必须为正数") |
@PositiveOrZero |
(新增) 数值类型 | 校验数值必须为正数或 0(>= 0) | @PositiveOrZero(message = "数量不能为负") |
@Negative |
(新增) 数值类型 | 校验数值必须为负数(< 0) | @Negative(message = "数值必须为负") |
@NegativeOrZero |
(新增) 数值类型 | 校验数值必须为负数或 0(<= 0) | @NegativeOrZero(message = "数值不能为正") |
| 逻辑校验 | |||
@AssertTrue |
(新增) Boolean | 校验字段值必须为 true |
@AssertTrue(message = "必须同意协议") |
@AssertFalse |
(新增) Boolean | 校验字段值必须为 false |
@AssertFalse(message = "逻辑状态必须为假") |
| 日期校验 | |||
@Future / @Past |
日期时间类型 | 校验日期是否在未来/过去 | @Future(message = "活动日期必须是未来") |
@FutureOrPresent |
(新增) 日期时间类型 | 校验日期是否在未来或当前时刻 | @FutureOrPresent(message = "日期不能早于现在") |
@PastOrPresent |
(新增) 日期时间类型 | 校验日期是否在过去或当前时刻 | @PastOrPresent(message = "出生日期不能在未来") |
| 格式校验 | |||
@Email |
String | 校验是否为有效的邮箱格式 | @Email(message = "邮箱格式不正确") |
@Pattern |
String | 校验字符串是否符合指定的正则表达式 | @Pattern(regexp="^1[3-9]\\d{9}$", message="手机号格式错误") |
自定义注解
定义注解
java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class) // 指定校验器
public @interface Phone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
实现校验逻辑
java
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && value.matches(PHONE_REGEX);
}
}
使用自定义注解
java
public class UserDTO {
@Phone
private String phoneNumber;
}
实战开发步骤
在DTO中定义校验规则
将校验注解直接添加到数据传输对象(DTO)的字段上,并可以通过 message 属性自定义错误信息。
java
public class UserCreateRequest {
@NotBlank(message = "用户名不能为空")
@Size(max = 50, message = "用户名长度不能超过50")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄必须大于等于18")
private Integer age;
// getters and setters...
}
在Controller层触发校验
在控制器方法的参数前使用 @Valid 或 @Validated 注解,即可触发对该参数的校验。
java
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@RequestBody @Valid UserCreateRequest request) {
// 如果校验通过,代码会执行到这里
return ResponseEntity.ok("用户创建成功");
}
}
@Valid 与 @Validated 的区别
| 特性 | @Valid (JSR-303/349 标准) |
@Validated (Spring 扩展) |
|---|---|---|
| 分组校验 | 不支持 | 支持 |
| 方法级校验 | 不支持 | 支持 (需配合 @Validated 在类上) |
| 嵌套对象校验 | 支持 (需在字段上使用) | 支持 (需在字段上使用) |
统一处理校验异常
校验失败会抛出 MethodArgumentNotValidException 异常。通常使用 @RestControllerAdvice 进行全局统一处理,向客户端返回结构化的错误信息。
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
// 收集所有字段错误信息
ex.getBindingResult().getFieldErrors().forEach(err ->
errors.put(err.getField(), err.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}
分组校验
分组校验允许你在不同场景下应用不同的校验规则,例如创建(Create)和更新(Update)操作。
定义分组接口
java
public class UserGroups {
public interface Create {}
public interface Update {}
}
在DTO中指定分组
java
public class UserDTO {
// 创建时ID必须为null,更新时ID不能为null
@Null(groups = UserGroups.Create.class)
@NotNull(groups = UserGroups.Update.class)
private Long id;
@NotBlank(groups = {UserGroups.Create.class, UserGroups.Update.class})
private String username;
// getters and setters...
}
在Controller中使用指定分组
java
@PostMapping("/create")
public String create(@RequestBody @Validated(UserGroups.Create.class) UserDTO user) { ... }
@PutMapping("/update")
public String update(@RequestBody @Validated(UserGroups.Update.class) UserDTO user) { ... }