使用示例
入门
web接口
@RestController
public class HelloController {
@PostMapping("/t1")
public void t1(@Validated @RequestBody Request request) {
System.out.println(11);
}
}
实体类
@Data
public class Request {
@NotEmpty(message = "title不为空")
private String title;
@NotEmpty(message = "name不为空")
private String name;
}
访问接口,会校验message
和name
的数据
自定义的校验器
注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy ={StatusValidator.class})
public @interface Status {
String[] statusType() default {};
String message() default "状态传递有误";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
StatusValidator
自定义的校验器
public class StatusValidator implements ConstraintValidator<Status, Integer> {
private List<String> typeStatus;
@Override
public void initialize(Status constraintAnnotation) {
typeStatus = Arrays.asList(constraintAnnotation.statusType());
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
if (value != null) {
if (!typeStatus.contains(String.valueOf(value))) {
return false;
}
}
return true;
}
}
在实体类上添加校验属性
@Data
public class Request {
@NotEmpty(message = "title不为空")
private String title;
@NotEmpty(message = "name不为空")
private String name;
@Status(statusType = {"1","2"})
private Integer loginStatus;
}
非web接口校验
@Service
@Validated
public class HelloService {
public void sayHello(@Valid Request request) {
System.out.println(request);
}
}
嵌套校验
实体类Request
,包含嵌套校验的属性User
@Data
public class Request {
@NotEmpty(message = "title不为空")
private String title;
@NotEmpty(message = "name不为空")
private String name;
@Status(statusType = {"1","2"})
private Integer loginStatus;
@Valid
@NotNull
private User user;
}
实体类
@Data
public class User {
@NotEmpty(message = "密码不能为空")
private String password;
@Max(value = 1)
private Integer age;
}
分组校验
校验属性设置分组
@Data
public class Request {
@NotEmpty(message = "title不为空", groups = Group1.class)
private String title;
@NotEmpty(message = "name不为空", groups = Group2.class)
private String name;
@Status(statusType = {"1","2"})
private Integer loginStatus;
private User user;
public interface Group1 {
}
public interface Group2 {
}
}
对参数进行分组校验
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@PostMapping("/t1")
public void t1(@Validated(Request.Group1.class) @RequestBody Request request) {
System.out.println(11);
}
}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
源码解析
web参数校验
RequestResponseBodyMethodProcessor#resolveArgument
,解析接口中的参数。判断校验结果是否有错,抛出异常。MethodArgumentNotValidException
属于BindException
的一种。
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
parameter = parameter.nestedIfOptional();
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
String name = Conventions.getVariableNameForParameter(parameter);
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
AbstractMessageConverterMethodArgumentResolver#validateIfApplicable
,进行校验
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
}
}
}
ValidationAnnotationUtils#determineValidationHints
,判断是否存在注解,Valid
和Validated
。
public static Object[] determineValidationHints(Annotation ann) {
Class<? extends Annotation> annotationType = ann.annotationType();
String annotationName = annotationType.getName();
if ("javax.validation.Valid".equals(annotationName)) {
return EMPTY_OBJECT_ARRAY;
}
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null) {
Object hints = validatedAnn.value();
return convertValidationHints(hints);
}
if (annotationType.getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
return convertValidationHints(hints);
}
return null;
}
private static Object[] convertValidationHints(@Nullable Object hints) {
if (hints == null) {
return EMPTY_OBJECT_ARRAY;
}
return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
}
DataBinder#validate(java.lang.Object...)
,遍历校验器。
public void validate(Object... validationHints) {
Object target = getTarget();
Assert.state(target != null, "No target to validate");
BindingResult bindingResult = getBindingResult();
// Call each validator with the same binding result
for (Validator validator : getValidators()) {
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, bindingResult, validationHints);
}
else if (validator != null) {
validator.validate(target, bindingResult);
}
}
}
ValidatorImpl#validate
,对实体类进行校验
public final <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
Contracts.assertNotNull(object, Messages.MESSAGES.validatedObjectMustNotBeNull());
this.sanityCheckGroups(groups);
Class<T> rootBeanClass = object.getClass();
BeanMetaData<T> rootBeanMetaData = this.beanMetaDataManager.getBeanMetaData(rootBeanClass);
if (!rootBeanMetaData.hasConstraints()) {
return Collections.emptySet();
} else {
BaseBeanValidationContext<T> validationContext = this.getValidationContextBuilder().forValidate(rootBeanClass, rootBeanMetaData, object);
ValidationOrder validationOrder = this.determineGroupValidationOrder(groups);
BeanValueContext<?, Object> valueContext = ValueContexts.getLocalExecutionContextForBean(this.validatorScopedContext.getParameterNameProvider(), object, validationContext.getRootBeanMetaData(), PathImpl.createRootPath());
return this.validateInContext(validationContext, valueContext, validationOrder);
}
}
ValidatorImpl#validateConstraintsForDefaultGroup
,根据组级别获取MetaConstraint
。
private <U> void validateConstraintsForDefaultGroup(BaseBeanValidationContext<?> validationContext, BeanValueContext<U, Object> valueContext) {
final BeanMetaData<U> beanMetaData = valueContext.getCurrentBeanMetaData();
final Map<Class<?>, Class<?>> validatedInterfaces = new HashMap<>();
// evaluating the constraints of a bean per class in hierarchy, this is necessary to detect potential default group re-definitions
for ( Class<? super U> clazz : beanMetaData.getClassHierarchy() ) {
BeanMetaData<? super U> hostingBeanMetaData = beanMetaDataManager.getBeanMetaData( clazz );
boolean defaultGroupSequenceIsRedefined = hostingBeanMetaData.isDefaultGroupSequenceRedefined();
// if the current class redefined the default group sequence, this sequence has to be applied to all the class hierarchy.
if ( defaultGroupSequenceIsRedefined ) {
Iterator<Sequence> defaultGroupSequence = hostingBeanMetaData.getDefaultValidationSequence( valueContext.getCurrentBean() );
Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getMetaConstraints();
while ( defaultGroupSequence.hasNext() ) {
for ( GroupWithInheritance groupOfGroups : defaultGroupSequence.next() ) {
boolean validationSuccessful = true;
for ( Group defaultSequenceMember : groupOfGroups ) {
validationSuccessful = validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz,
metaConstraints, defaultSequenceMember ) && validationSuccessful;
}
validationContext.markCurrentBeanAsProcessed( valueContext );
if ( !validationSuccessful ) {
break;
}
}
}
}
// fast path in case the default group sequence hasn't been redefined
else {
Set<MetaConstraint<?>> metaConstraints = hostingBeanMetaData.getDirectMetaConstraints();
validateConstraintsForSingleDefaultGroupElement( validationContext, valueContext, validatedInterfaces, clazz, metaConstraints,
Group.DEFAULT_GROUP );
validationContext.markCurrentBeanAsProcessed( valueContext );
}
// all constraints in the hierarchy has been validated, stop validation.
if ( defaultGroupSequenceIsRedefined ) {
break;
}
}
}
ValidatorImpl#validateConstraintsForSingleDefaultGroupElement
,对于每一个MetaConstraint
执行校验
private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanValidationContext<?> validationContext, ValueContext<U, Object> valueContext, final Map<Class<?>, Class<?>> validatedInterfaces,
Class<? super U> clazz, Set<MetaConstraint<?>> metaConstraints, Group defaultSequenceMember) {
boolean validationSuccessful = true;
valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );
for ( MetaConstraint<?> metaConstraint : metaConstraints ) {
// HV-466, an interface implemented more than one time in the hierarchy has to be validated only one
// time. An interface can define more than one constraint, we have to check the class we are validating.
final Class<?> declaringClass = metaConstraint.getLocation().getDeclaringClass();
if ( declaringClass.isInterface() ) {
Class<?> validatedForClass = validatedInterfaces.get( declaringClass );
if ( validatedForClass != null && !validatedForClass.equals( clazz ) ) {
continue;
}
validatedInterfaces.put( declaringClass, clazz );
}
boolean tmp = validateMetaConstraint( validationContext, valueContext, valueContext.getCurrentBean(), metaConstraint );
if ( shouldFailFast( validationContext ) ) {
return false;
}
validationSuccessful = validationSuccessful && tmp;
}
return validationSuccessful;
}
SimpleConstraintTree#validateConstraints
,获取校验器。
@Override
protected void validateConstraints(ValidationContext<?> validationContext,
ValueContext<?, ?> valueContext,
Collection<ConstraintValidatorContextImpl> violatedConstraintValidatorContexts) {
if ( LOG.isTraceEnabled() ) {
LOG.tracef(
"Validating value %s against constraint defined by %s.",
valueContext.getCurrentValidatedValue(),
descriptor
);
}
// find the right constraint validator
ConstraintValidator<B, ?> validator = getInitializedConstraintValidator( validationContext, valueContext );
// create a constraint validator context
ConstraintValidatorContextImpl constraintValidatorContext = validationContext.createConstraintValidatorContextFor(
descriptor, valueContext.getPropertyPath()
);
// validate
if ( validateSingleConstraint( valueContext, constraintValidatorContext, validator ).isPresent() ) {
violatedConstraintValidatorContexts.add( constraintValidatorContext );
}
}
ConstraintValidatorManagerImpl#getInitializedValidator
,获取校验器
public <A extends Annotation> ConstraintValidator<A, ?> getInitializedValidator(
Type validatedValueType,
ConstraintDescriptorImpl<A> descriptor,
ConstraintValidatorFactory constraintValidatorFactory,
HibernateConstraintValidatorInitializationContext initializationContext) {
Contracts.assertNotNull( validatedValueType );
Contracts.assertNotNull( descriptor );
Contracts.assertNotNull( constraintValidatorFactory );
Contracts.assertNotNull( initializationContext );
CacheKey key = new CacheKey( descriptor.getAnnotationDescriptor(), validatedValueType, constraintValidatorFactory, initializationContext );
@SuppressWarnings("unchecked")
ConstraintValidator<A, ?> constraintValidator = (ConstraintValidator<A, ?>) constraintValidatorCache.get( key );
if ( constraintValidator == null ) {
constraintValidator = createAndInitializeValidator( validatedValueType, descriptor, constraintValidatorFactory, initializationContext );
constraintValidator = cacheValidator( key, constraintValidator );
}
else {
LOG.tracef( "Constraint validator %s found in cache.", constraintValidator );
}
return DUMMY_CONSTRAINT_VALIDATOR == constraintValidator ? null : constraintValidator;
}
protected <A extends Annotation> ConstraintValidator<A, ?> createAndInitializeValidator(
Type validatedValueType,
ConstraintDescriptorImpl<A> descriptor,
ConstraintValidatorFactory constraintValidatorFactory,
HibernateConstraintValidatorInitializationContext initializationContext) {
ConstraintValidatorDescriptor<A> validatorDescriptor = findMatchingValidatorDescriptor( descriptor, validatedValueType );
ConstraintValidator<A, ?> constraintValidator;
if ( validatorDescriptor == null ) {
return null;
}
constraintValidator = validatorDescriptor.newInstance( constraintValidatorFactory );
initializeValidator( descriptor, constraintValidator, initializationContext );
return constraintValidator;
}
ConstraintTree#validateSingleConstraint
,用校验器执行isValid
方法
protected final <V> Optional<ConstraintValidatorContextImpl> validateSingleConstraint(
ValueContext<?, ?> valueContext,
ConstraintValidatorContextImpl constraintValidatorContext,
ConstraintValidator<A, V> validator) {
boolean isValid;
try {
@SuppressWarnings("unchecked")
V validatedValue = (V) valueContext.getCurrentValidatedValue();
isValid = validator.isValid( validatedValue, constraintValidatorContext );
}
catch (RuntimeException e) {
if ( e instanceof ConstraintDeclarationException ) {
throw e;
}
throw LOG.getExceptionDuringIsValidCallException( e );
}
if ( !isValid ) {
//We do not add these violations yet, since we don't know how they are
//going to influence the final boolean evaluation
return Optional.of( constraintValidatorContext );
}
return Optional.empty();
}
非web接口校验
ValidationAutoConfiguration
会注入MethodValidationPostProcessor
@Bean
@ConditionalOnMissingBean(
search = SearchStrategy.CURRENT
)
public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator, ObjectProvider<MethodValidationExcludeFilter> excludeFilters) {
FilteredMethodValidationPostProcessor processor = new FilteredMethodValidationPostProcessor(excludeFilters.orderedStream());
boolean proxyTargetClass = (Boolean)environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
MethodValidationPostProcessor#afterPropertiesSet
,创建切面和拦截器。
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
@Override
public void afterPropertiesSet() {
Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
}
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
}
AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
,MethodValidationPostProcessor
继承了AbstractAdvisingBeanPostProcessor
,添加到advised 中,通过ProxyFactory
返回代理对象。
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
return bean;
}
MethodValidationInterceptor#invoke
,进行方法校验,校验不通过则报错。可以对参数和返回值进行校验。
@Override
@Nullable
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;
Object target = invocation.getThis();
Assert.state(target != null, "Target must not be null");
try {
result = execVal.validateParameters(target, 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(), target.getClass()));
result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
}
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
Object returnValue = invocation.proceed();
result = execVal.validateReturnValue(target, methodToValidate, returnValue, groups);
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
return returnValue;
}
MethodValidationInterceptor#determineValidationGroups
,获取方法或者类上的注解Validated
。
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class);
if (validatedAnn == null) {
Object target = invocation.getThis();
Assert.state(target != null, "Target must not be null");
validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
}
return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
}
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理
标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数校验原理