Spring Framework 供给了一套能够方便地对 Controller 层中接纳的参数进行校验的结构,其中就包含了 @Validated 注解。在 Spring 项目中运用 @Validated 注解能够让咱们愈加方便地进行参数校验,避免了手动校验的费事,而且使得代码愈加高雅和易于保护。本文将详细介绍在 Spring 项目中运用 @Validated 进行参数校验的办法和常见运用场景。
一、@Validated 注解简介
@Validated 注解是 Spring Framework 中供给的一个参数校验注解,它能够用来符号需求进行参数校验的办法、类、办法参数和办法返回值等当地。经过运用 @Validated 注解,咱们能够非常方便地对入参进行检查,而且能够自界说校验规矩和过错提示信息。
引进依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
二、@Validated 注解的运用
2.1 在 Controller 层中运用
在 Controller 层中运用 @Validated 注解是最为常见的运用场景。经过在 Controller 办法的参数上增加 @Validated 注解,能够对该参数进行校验。下面是一个简单的例子:
@RestController
@RequestMapping("/api/user")
public class UserController {
@PostMapping
public User createUser(@RequestBody @Validated User user) {
// ...
}
}
在上述代码中,@Validated 注解符号了 User 类型的参数,表明需求对该参数进行校验。假如 User 类中存在校验注解,那么这些注解会自动触发校验过程。假如校验不经过,则会抛出 MethodArgumentNotValidException 反常。
2.2 自界说校验规矩
在运用 @Validated 注解进行参数校验时,咱们常常需求自界说校验规矩。Spring Framework 供给了多种自界说校验规矩的办法,包含运用注解完结、编写自界说 Validator 等办法。
2.2.1 运用注解完结
能够经过编写注解来进行自界说校验规矩的完结。例如,下面是一个用于校验手机号码格局的注解:
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "手机号码格局不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
在上述代码中,@Phone 注解用于符号需求进行手机号码格局校验的字段,而 @Constraint 注解指定了详细的校验逻辑类 PhoneValidator。PhoneValidator 完结了 ConstraintValidator 接口,完结对手机号码格局的校验。
接下来,咱们来看一下 PhoneValidator 的完结:
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private final static Pattern PHONE_PATTERN = Pattern.compile("^1\\d{10}$");
@Override
public void initialize(Phone constraintAnnotation) {
}
@Override
public boolean isValid(String phone, ConstraintValidatorContext context) {
return StringUtils.isEmpty(phone) || PHONE_PATTERN.matcher(phone).matches();
}
}
在 PhoneValidator 的 isValid 办法中,咱们运用了正则表达式来判别手机号码的格局是否正确。假如格局不正确,则返回 false,而且能够经过 context 参数来设置过错提示信息。
运用上述自界说注解完结的校验规矩,能够和 Spring 自带的校验注解相同,方便地被运用到 Controller 层中。
2.2.2 编写自界说 Validator
除了运用注解来完结自界说校验规矩以外,还能够编写自界说 Validator 来完结详细的校验逻辑。
下面是一个简单的示例,用于校验两个整数的巨细联系:
public class ComparisonValidator implements ConstraintValidator<Comparison, Object> {
private Comparison.Operator operator;
private String valueFieldName;
@Override
public void initialize(Comparison constraintAnnotation) {
operator = constraintAnnotation.operator();
valueFieldName = constraintAnnotation.valueFieldName();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
try {
Field valueField = value.getClass().getDeclaredField(valueFieldName);
valueField.setAccessible(true);
Object otherValue = valueField.get(value);
if (otherValue == null) {
return true;
}
int result = ((Comparable) value).compareTo(otherValue);
switch (operator) {
case GREATER_THAN:
return result > 0;
case LESS_THAN:
return result < 0;
case GREATER_THAN_OR_EQUAL_TO:
return result >= 0;
case LESS_THAN_OR_EQUAL_TO:
return result <= 0;
default:
throw new IllegalArgumentException("Unsupported Comparison Operator: " + operator);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
在上述代码中,咱们界说了一个 ComparisonValidator 类,它完结了 ConstraintValidator 接口,而且自界说了一个校验规矩 @Comparison。
该校验规矩需求经过 operator 和 valueFieldName 两个特点来确定详细的比较办法和被比较的特点名称。在 isValid 办法中,咱们首要获取到被比较的特点值 otherValue,然后根据 operator 来判别 value 是否大于(小于、等于)otherValue。
运用自界说 Validator 需求手动创建校验逻辑类,并将其与注解进行相关。在 Controller 层运用时,咱们能够像运用 Spring 自带的校验注解相同来运用自界说的校验注解。
2.3 组合注解
有时候,咱们需求对同一个参数进行多种校验,这时候能够运用组合注解的办法来完结。例如,下面是一个用于校验暗码格局的组合注解示例:
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$", message = "暗码有必要包含巨细写字母、数字和特别字符,长度至少为 8 位")
@NotNull(message = "暗码不能为空")
public @interface Password {
String message() default "暗码格局不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
在上述代码中,@Password 注解经过组合 @Pattern 和 @NotNull 注解来完结了暗码的格局校验。假如暗码格局不正确或者为空,则会抛出校验反常。
在运用组合注解时,需求注意被组合的注解是否已经运用了 @Constraint 注解,而且不要忘记设置 message、groups 和 payload 等特点。
2.4 统一反常处理
在运用 @Validated 进行参数校验时,假如校验失利,会抛出 MethodArgumentNotValidException 反常。为了提高代码的可保护性,咱们能够经过在 Controller 层增加 @ExceptionHandler 注解并捕获该反常,来统一处理校验失利的情况。
例如,下面是一个简单的反常处理示例:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, Object> handleValidationException(MethodArgumentNotValidException ex) {
BindingResult bindingResult = ex.getBindingResult();
List<String> errorList = bindingResult.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return Collections.singletonMap("message", errorList);
}
}
在上述代码中,咱们经过在 GlobalExceptionHandler 类中增加 @RestControllerAdvice 和 @ExceptionHandler 注解来统一处理 MethodArgumentNotValidException 反常。在 handleValidationException 办法中,咱们首要获取到 BindingResult 对象,并经过遍历一切过错信息来搜集过错提示信息。最终返回一个包含过错提示信息的 Map 对象。
三、@Validated 注解的常见运用场景
@Validated 注解作为 Spring Framework 中的一个参数校验注解,广泛运用于 Controller 层的参数校验、DTO 类的参数校验和事务类的参数校验等方面。下面列举了几个常见的运用场景:
-
数据库操作时的参数校验:数据库操作一般需求对参数进行校验,避免因为无效参数导致的 SQL 注入等安全问题。
-
DTO 类的参数校验:在运用 DTO(Data Transfer Object)类进行数据传输时,往往需求对传输的字段进行校验,保证数据的有效性和完整性。
-
事务类的参数校验:事务类中的办法通常也需求对参数进行校验,以保证事务逻辑的正确性和可靠性。
四、常用的验证注解
- @NotNull 注解
@NotNull 表明被注解的参数不能为 null。
例如:
public void testNotNull(@NotNull String str) {}
- @Size 注解
@Size 表明被注解的参数的巨细有必要在指定的范围内(包含最小值和最大值)。
例如:
public void testSize(@Size(min = 1, max = 10) String str) {}
- @Min 和 @Max 注解
@Min 和 @Max 别离表明被注解的参数的最小值和最大值。
例如:
public void testMin(@Min(18) int age) {}
public void testMax(@Max(100) int score) {}
- @DecimalMin 和 @DecimalMax 注解
@DecimalMin 和 @DecimalMax 别离表明被注解的参数的最小值和最大值,适用于浮点数、BigDecimal 或 BigInteger 类型的参数。
例如:
public void testDecimalMin(@DecimalMin("0.00") BigDecimal price) {}
public void testDecimalMax(@DecimalMax("100.00") BigDecimal score) {}
- @Digits 注解
@Digits 表明被注解的参数有必要是一个数字,而且整数位和小数位的位数不能超过指定的值(默认整数位 2 位,小数位 0 位)。
例如:
public void testDigits(@Digits(integer = 2, fraction = 1) BigDecimal num) {}
- @Email 注解
@Email 表明被注解的参数有必要是一个合法的电子邮件地址。
例如:
public void testEmail(@Email String email) {}
- @Pattern 注解
@Pattern 表明被注解的参数有必要符合指定的正则表达式形式。
例如:
public void testPattern(@Pattern(regexp = "^\\d{4}-\\d{1,2}-\\d{1,2}$") String date) {}
除了运用单个注解外,也能够运用组合注解来完结更为复杂的校验逻辑。
例如:
@NotNull(message = "用户名不能为空")
@Size(min = 5, max = 20, message = "用户名长度有必要在 5 到 20 之间")
public String getUsername() {
return this.username;
}
上述代码表明要求用户名不能为空,而且在长度范围内。假如不符合要求,则会抛出相应的反常,如 MethodArgumentNotValidException 等。