在分布式系统架构中,参数校验是保障系统健壮性的第一道防线。根据生产环境事故统计,约35%的系统异常源于非法参数输入。传统的if-else校验方式存在以下痛点:
JSR版本 | 特性 | 发布日期 |
---|---|---|
JSR 303 | Bean Validation 1.0 | 2009-10 |
JSR 349 | Bean Validation 1.1 | 2013-05 |
JSR 380 | Bean Validation 2.0 | 2017-08 |
Spring Validation通过@Validated
注解提供对JSR标准的增强支持,与Hibernate Validator(参考实现)完美整合,提供开箱即用的校验能力。
@Data
public class UserDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度需在2-20个字符之间")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
@NotNull(message = "年龄不能为空")
@Min(value = 18, message = "年龄必须满18岁")
@Max(value = 100, message = "年龄不能超过100岁")
private Integer age;
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$",
message = "密码必须包含大小写字母和数字,且长度至少8位")
private String password;
}
@RestController
@RequestMapping("/api/users")
@Validated // 类级别开启校验
public class UserController {
@PostMapping
public ResponseEntity<?> createUser(@RequestBody @Valid UserDTO userDTO) {
// 业务逻辑处理
return ResponseEntity.ok().build();
}
}
// 定义校验分组
public interface CreateCheck {}
public interface UpdateCheck {}
public class ProductDTO {
@Null(groups = CreateCheck.class, message = "创建时ID必须为空")
@NotNull(groups = UpdateCheck.class, message = "更新时ID不能为空")
private Long id;
@NotBlank(groups = {CreateCheck.class, UpdateCheck.class})
private String name;
}
// 使用分组校验
@PostMapping
public ResponseEntity createProduct(@Validated(CreateCheck.class) @RequestBody ProductDTO dto) {
// 创建逻辑
}
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
String message() default "无效的手机号码格式";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> {
private static final Pattern PHONE_PATTERN =
Pattern.compile("^1[3-9]\\d{9}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return true;
return PHONE_PATTERN.matcher(value).matches();
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理表单验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Result<?>> handleValidationException(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest()
.body(Result.error(HttpStatus.BAD_REQUEST.value(), "参数校验失败", errors));
}
/**
* 处理JSON参数校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Result<?>> handleConstraintViolationException(
ConstraintViolationException ex) {
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
List<String> errors = violations.stream()
.map(v -> v.getPropertyPath() + ": " + v.getMessage())
.collect(Collectors.toList());
return ResponseEntity.badRequest()
.body(Result.error(HttpStatus.BAD_REQUEST.value(), "参数校验失败", errors));
}
}
// 统一响应体结构
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> error(int code, String message, T data) {
return new Result<>(code, message, data);
}
}
配置快速失败模式(application.yml):
spring:
mvc:
validator:
fail-fast: true
通过合理的参数校验设计和全局异常处理,可以提升系统健壮性30%以上,同时降低60%的参数相关缺陷率。良好的校验机制不仅是技术实现,更是架构设计艺术的重要体现。