01 依赖配置
在构建高效的校验体系前,需先完善项目依赖配置。
以下是优化后的依赖示例:
<dependencies>
<!-- Web 依赖,提供 RESTful 接口支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 校验核心依赖,整合 Hibernate Validator 实现 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
注解:
- spring-boot-starter-web 依赖引入了 Spring MVC 组件,为构建 Web 应用提供基础支持
- spring-boot-starter-validation 依赖整合了 Hibernate Validator 实现,提供 JSR 380 校验规范支持
02 校验注解体系构建
构建完善的校验注解体系是实现数据校验的基础。
以下是企业级校验注解应用示例:
import javax.validation.constraints.*;
import java.time.LocalDate;
public class UserRegistrationRequest {
// 用户名校验:非空且长度在 4-20 位之间
@NotBlank(message = "用户名不能为空")
@Size(min = 4, max = 20, message = "用户名长度需在 4-20 位之间")
private String username;
// 邮箱校验:非空且符合邮箱格式
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
// 密码校验:非空且长度不少于 8 位
@NotBlank(message = "密码不能为空")
@Size(min = 8, message = "密码长度至少为 8 位")
private String password;
// 年龄校验:必须大于 0 且小于 150
@Min(value = 1, message = "年龄必须大于 0")
@Max(value = 150, message = "年龄不能超过 150")
private Integer age;
// 出生日期校验:不能晚于当前日期
@PastOrPresent(message = "出生日期不能晚于当前日期")
private LocalDate birthDate;
// 协议同意校验:必须为 true
@NotNull(message = "必须同意服务条款")
private Boolean termsAccepted;
}
注解:
- 使用组合注解实现多维度校验,如 @NotBlank + @Size 实现既校验非空又校验长度的双重校验
- 通过明确的错误提示信息,提升系统友好性,便于用户定位问题
- 日期类型使用 @PastOrPresent 校验,避免接收未来日期的非法输入
03 控制器校验集成
在控制器层面集成校验逻辑,确保所有入参经过严格校验:
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@Validated // 启用方法参数校验
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/register")
public ResponseEntity registerUser(@Valid @RequestBody UserRegistrationRequest request) {
// 业务逻辑处理
return ResponseEntity.ok("用户注册成功");
}
@GetMapping("/{id}")
public ResponseEntity getUserById(@PathVariable Long id) {
// 用户查询逻辑
return ResponseEntity.ok(new User(id, "示例用户"));
}
}
注解:
@Validated 注解启用控制器方法参数校验,配合 @Valid 对请求体进行深度校验
@RequestBody 结合校验注解,实现复杂对象的自动绑定与校验
通过明确的 HTTP 状态码和响应消息,提供标准化的接口反馈
04 全局异常处理机制
构建完善的全局异常处理机制,统一管理校验异常:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return ResponseEntity.badRequest().body(errors);
}
@ExceptionHandler(Exception.class)
public ResponseEntity handleGeneralException(Exception ex) {
return ResponseEntity.internalServerError().body("系统内部错误");
}
}
注解:
- 通过 @RestControllerAdvice 实现全局异常处理,集中管理各类异常
- 针对校验异常提取字段级错误信息,返回结构化的错误响应
- 提供默认异常处理方法,确保所有未被捕获的异常都能得到妥善处理
05 分组校验实现
在复杂业务场景中,通过分组校验实现条件校验逻辑:
// 定义校验分组接口
public interface CreateGroup {}
public interface UpdateGroup {}
// 应用分组校验的 DTO 类
public class UserDTO {
@NotBlank(groups = CreateGroup.class, message = "创建时用户名必填")
@Size(groups = CreateGroup.class, min = 4, message = "用户名长度至少为 4")
private String username;
@Email(groups = {CreateGroup.class, UpdateGroup.class}, message = "邮箱格式不正确")
private String email;
// Getter 和 Setter 方法
}
// 控制器方法应用分组校验
@PostMapping("/users")
public ResponseEntity createUser(@Validated({CreateGroup.class}) UserDTO userDTO) {
// 创建用户逻辑
return ResponseEntity.ok("用户创建成功");
}
@PutMapping("/users/{id}")
public ResponseEntity updateUser(@Validated({UpdateGroup.class}) UserDTO userDTO) {
// 更新用户逻辑
return ResponseEntity.ok("用户更新成功");
}
注解:
通过定义接口实现校验分组,灵活控制不同场景下的校验规则
在 DTO 类中指定各字段所属的校验分组,实现条件校验
控制器方法通过指定校验分组,精确控制校验逻辑的触发场景
06 嵌套对象校验
处理复杂对象嵌套场景下的校验需求:
// 嵌套对象校验示例
public class Order {
@NotBlank(message = "订单号不能为空")
private String orderId;
@DecimalMin(value = "0.01", message = "订单金额必须大于 0.01")
private BigDecimal amount;
// 嵌套 Customer 对象,启用深度校验
@Valid
private Customer customer;
}
public class Customer {
@NotBlank(message = "客户名称不能为空")
private String name;
@Email(message = "邮箱格式不正确")
private String email;
// 嵌套 Address 对象,继续深度校验
@Valid
private Address address;
}
public class Address {
@NotBlank(message = "地址不能为空")
private String street;
@NotBlank(message = "城市不能为空")
private String city;
}
// 控制器方法应用嵌套校验
@PostMapping("/orders")
public ResponseEntity createOrder(@Valid @RequestBody Order order) {
// 订单创建逻辑
return ResponseEntity.ok("订单创建成功");
}
注解:
- 使用 @Valid 注解启用嵌套对象的深度校验,支持多层对象嵌套
- 校验规则沿用对象关系,自动继承父对象的校验上下文
- 全局异常处理器自动捕获嵌套校验错误,返回详细的错误路径信息
07 自定义校验注解开发
针对特殊业务场景开发自定义校验注解:
// 自定义注解定义
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface ValidPhoneNumber {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 自定义校验实现
public class PhoneValidator implements ConstraintValidator<ValidPhoneNumber, String> {
private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
@Override
public void initialize(ValidPhoneNumber constraintAnnotation) {
// 初始化逻辑
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null || value.trim().isEmpty()) {
return true; // 空值不校验
}
return Pattern.matches(PHONE_REGEX, value);
}
}
// 自定义注解应用示例
public class ContactInfo {
@ValidPhoneNumber
private String phone;
// Getter 和 Setter 方法
}
注解:
- 通过 @Constraint 注解指定校验器实现类,完成自定义校验注解开发
- 校验器实现类需继承 ConstraintValidator,重写 isValid 方法定义校验逻辑
- 自定义注解可应用于字段或参数,与内置注解使用方式一致