mave依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
注解使用场景
一、注解修饰方法的参数
eg.
public void a(@NotEmpty String userName){
…
}
这一类的情况,需要在类上或者方法上添加@Valid或者@Validated注解
二、注解修饰参数实体的属性
class ValidVo{
@NotNull
String name;
}
eg.
public void a(@Valid ValidVo userName){
…
}
这一类的情况,需要在方法参数添加@Valid或者@Validated注解
注意事项
校验参数的注解指针对controller层的方法有效,对service层的方法无效;
常见的约束注解如下:
注解 功能
@AssertFalse 可以为null,如果不为null的话必须为false
@AssertTrue 可以为null,如果不为null的话必须为true
@DecimalMax 设置不能超过最大值
@DecimalMin 设置不能超过最小值
@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内
@Future 日期必须在当前日期的未来
@Past 日期必须在当前日期的过去
@Max 最大不得超过此最大值
@Min 最大不得小于此最小值
@NotNull 不能为null,可以是空
@Null 必须为null
@Pattern 必须满足指定的正则表达式
@Size 集合、数组、map等的size()值必须在指定范围内
@Email 必须是email格式
@Length 长度必须在指定范围内
@NotBlank 字符串不能为null,字符串trim()后也不能等于“”
@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”
@Range 值必须在指定范围内
@URL 必须是一个URL
springboot校验参数源码
对于第一种情况参数校验的逻辑由MethodValidationInterceptor进行校验
public class MethodValidationInterceptor implements MethodInterceptor {
...
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// Avoid Validator invocation on FactoryBean.getObjectType/isSingleton
if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
return invocation.proceed();
}
Class<?>[] groups = determineValidationGroups(invocation);
// Standard Bean Validation 1.1 API
ExecutableValidator execVal = this.validator.forExecutables();
Method methodToValidate = invocation.getMethod();
Set<ConstraintViolation<Object>> result;
try {
result = execVal.validateParameters(
invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
}
catch (IllegalArgumentException ex) {
// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
// Let's try to find the bridged method on the implementation class...
methodToValidate = BridgeMethodResolver.findBridgedMethod(
ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
result = execVal.validateParameters(
invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
}
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
Object returnValue = invocation.proceed();
result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
return returnValue;
}
...
}
主要代码
ExecutableValidator execVal = this.validator.forExecutables();
execVal.validateParameters(
invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
第二种情况会在WebDataBind的过程由ModelAttributeMethodProcessor进行参数校验
单独使用Validator校验参数
Validator validator = Validation.byDefaultProvider().configure()
.messageInterpolator(new ResourceBundleMessageInterpolator(
new PlatformResourceBundleLocator("validationMessages"))) //手动指定校验提示资源(默认在resource目录下ValidationMessages.properties)
.buildValidatorFactory().getValidator();
Set<ConstraintViolation<Object>> constraintViolations = smartValidator.forExecutables().validateParameters(joinPoint.getTarget(), signature.getMethod(), args);
注解必须使用JSR的注解,spring的Validated注解不会生效
springboot中使用aop校验非controller层参数
注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ValidParam {
Class<?>[] value() default {};
}
切面逻辑
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
@Aspect
@Component
@Slf4j
public class ParamsCheckAspect {
final Validator smartValidator;
public ParamsCheckAspect(Validator smartValidator) {
this.smartValidator = smartValidator;
}
@Before("@within(validParam)||@annotation(validParam)")
public void before(JoinPoint joinPoint, ValidParam validParam) {
log.debug("校验参数 {} ,{} ", joinPoint, validParam);
Object[] args = joinPoint.getArgs();
if (args.length == 0)
return;
Class<?>[] groups = new Class[0];
if (validParam != null)
groups = validParam.value();
log.debug("参数分组 {} ", Arrays.deepToString(groups));
Set<ConstraintViolation<Object>> constraintViolations;
constraintViolations = smartValidator.forExecutables().validateParameters(joinPoint.getTarget(), ((MethodSignature) joinPoint.getSignature()).getMethod(), args, groups);
log.debug("校验结果 {}", constraintViolations);
errorResult(constraintViolations);
}
private void errorResult(Set<ConstraintViolation<Object>> constraintViolations) {
StringBuilder errMessage = new StringBuilder();
if (constraintViolations.size() > 0) {
throw new ValidationException(constraintViolations.stream()
.map(error -> {
StringBuilder propertyPath = new StringBuilder().append(error.getRootBeanClass().getSimpleName()).append(".").append(error.getPropertyPath());
return errMessage.append(error.getMessage()).append(error.getMessage()).append("(").append(propertyPath).append(")");
})
.collect(Collectors.joining(",")));
}
}
}