小架构step系列26:Spring提供的validator

1 概述

对于Web服务,需要对请求的参数进行校验,可以对不合法的参数进行提示,提高用户体验。也可以防止有人恶意用一些非法的参数对网站造成破坏。如果是对每个参数都写一段代码来判断值是否合法,那校验的代码就很多,也很繁琐。Spring提供了一套校验机制,先来了解一下。

2 原理

2.1 初始化mvcValidator

初始化mvcValidator,mvcValidator是一个SpringValidatorAdapter,其里面的targetValidator是真正执行validate校验的对象,初始化mvcValidator的主要目的就是要把targetValidator组装好。

java 复制代码
// 源码位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
// EnableWebMvcConfiguration是带@Configuration注解的类,当开启了WebMvc就会加载
@Bean
public Validator mvcValidator() {
    if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
        return super.mvcValidator();
    }
    // 1. 调getValidator()获取Validator
    //    EnableWebMvcConfiguration继承DelegatingWebMvcConfiguration,调的是DelegatingWebMvcConfiguration的getValidator()
    return ValidatorAdapter.get(getApplicationContext(), getValidator());
}

// 源码位置:org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
protected Validator getValidator() {
    // 2. configures就是平时熟悉的WebMvcConfigurer的封装(WebMvcConfigurerComposite)
    return this.configurers.getValidator();
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
public Validator getValidator() {
    Validator selected = null;
    for (WebMvcConfigurer configurer : this.delegates) {
        // 3. 如果定义了WebMvcConfigurer,则实际调WebMvcConfigurer的getValidator()
        //    WebMvcConfigurer是Spring提供的常用的扩展方式,可通过它来增加Validator,此时没有定义返回的是null
        Validator validator = configurer.getValidator();
        if (validator != null) {
            if (selected != null) {
                throw new IllegalStateException("No unique Validator found: {" +
                        selected + ", " + validator + "}");
            }
            selected = validator;
        }
    }
    return selected;
}

// 回到EnableWebMvcConfiguration的mvcValidator(),处理ValidatorAdapter.get()
// 源码位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
@Bean
public Validator mvcValidator() {
    if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
        return super.mvcValidator();
    }
    // 1. 调getValidator()获取Validator
    // 4. getValidator()获取的Validator作为默认的,继续由ValidatorAdapter.get()处理
    return ValidatorAdapter.get(getApplicationContext(), getValidator());
}


// 源码位置:org.springframework.boot.autoconfigure.validation.ValidatorAdapter
public static Validator get(ApplicationContext applicationContext, Validator validator) {
    // 如果获取到了Validator,优先封装到ValidatorAdapter里面并返回ValidatorAdapter,这里主要看没有获取到Validator的场景
    if (validator != null) {
        return wrap(validator, false);
    }
    // 5. 获取一个存在的,如果没有就创建一个
    return getExistingOrCreate(applicationContext);
}
private static Validator getExistingOrCreate(ApplicationContext applicationContext) {
    // 6. 先获取已经存在的Validator
    Validator existing = getExisting(applicationContext);
    if (existing != null) {
        return wrap(existing, true);
    }
    return create(applicationContext);
}
private static Validator getExisting(ApplicationContext applicationContext) {
    try {
        // 7. 获取实现了org.springframework.validation.Validator接口的bean,这里主要看获取不到的场景,也就是Spring默认没有注入实现了该接口的bean
        javax.validation.Validator validator = applicationContext.getBean(javax.validation.Validator.class);
        if (validator instanceof Validator) {
            return (Validator) validator;
        }
        return new SpringValidatorAdapter(validator);
    }
    catch (NoSuchBeanDefinitionException ex) {
        return null; // 获取不到从这里返回null
    }
}

// 回到ValidatorAdapter的getExistingOrCreate()继续处理
// 源码位置:org.springframework.boot.autoconfigure.validation.ValidatorAdapter
private static Validator getExistingOrCreate(ApplicationContext applicationContext) {
    // 6. 先获取已经存在的Validator
    Validator existing = getExisting(applicationContext);
    if (existing != null) {
        return wrap(existing, true);
    }
    
    // 8. 没有存在的就创建一个
    return create(applicationContext);
}
private static Validator create(MessageSource messageSource) {
    // 9. 直接创建一个OptionalValidatorFactoryBean,它是Validator的一个工厂bean
    //    继承关系:OptionalValidatorFactoryBean < LocalValidatorFactoryBean < SpringValidatorAdapter < javax.validation.Validator
    OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean();
    try {
        MessageInterpolatorFactory factory = new MessageInterpolatorFactory(messageSource);
        validator.setMessageInterpolator(factory.getObject());
    }
    catch (ValidationException ex) {
    }
    
    return wrap(validator, false);
}

// 10. OptionalValidatorFactoryBean的父类SpringValidatorAdapter里有个关键的对象targetValidator,
//     实际的validate工作都是由它完成的,但上面OptionalValidatorFactoryBean()创建的时候使用的是无参构造,
//     则SpringValidatorAdapter里面的targetValidator也没有赋值,所以还做不了实际的validate工作
// 源码位置:org.springframework.validation.beanvalidation.SpringValidatorAdapter
public class SpringValidatorAdapter implements SmartValidator, javax.validation.Validator {
    private javax.validation.Validator targetValidator;
    
    public SpringValidatorAdapter(javax.validation.Validator targetValidator) {
        Assert.notNull(targetValidator, "Target Validator must not be null");
        this.targetValidator = targetValidator;
    }

    SpringValidatorAdapter() {
    }

    void setTargetValidator(javax.validation.Validator targetValidator) {
        this.targetValidator = targetValidator;
    }
    
    public void validate(Object target, Errors errors) {
        // 由targetValidator进行实际的validate工作
        if (this.targetValidator != null) {
            processConstraintViolations(this.targetValidator.validate(target), errors);
        }
    }
    // 省略其它代码
}

// 回到ValidatorAdapter的create()继续处理
// 源码位置:org.springframework.boot.autoconfigure.validation.ValidatorAdapter
private static Validator create(MessageSource messageSource) {
    // 9. 直接创建一个OptionalValidatorFactoryBean,它是Validator的一个工厂bean
    OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean();
    try {
        MessageInterpolatorFactory factory = new MessageInterpolatorFactory(messageSource);
        validator.setMessageInterpolator(factory.getObject());
    }
    catch (ValidationException ex) {
    }
    
    // 11. 把Validator包装到ValidatorAdapter返回
    return wrap(validator, false);
}

// 源码位置:org.springframework.boot.autoconfigure.validation.ValidatorAdapter
private static Validator wrap(Validator validator, boolean existingBean) {
    if (validator instanceof javax.validation.Validator) {
        // 12. 从上面继承关系可以看到OptionalValidatorFactoryBean是SpringValidatorAdapter子类,if条件成立
        if (validator instanceof SpringValidatorAdapter) {
            return new ValidatorAdapter((SpringValidatorAdapter) validator, existingBean);
        }
        
        // 如果自定义提供一个实现了Validator接口且不是SpringValidatorAdapter子类的validator,则封装到SpringValidatorAdapter里
        return new ValidatorAdapter(new SpringValidatorAdapter((javax.validation.Validator) validator),
                existingBean);
    }
    
    // 使用WebMvcConfigurer提供的属于org.springframework.validation.Validator类型,不包装到SpringValidatorAdapter而直接作为mvcValidator
    return validator;
}

// 回到EnableWebMvcConfiguration的mvcValidator()
// 从这个过程看,目前只是创建了OptionalValidatorFactoryBean(包装到了ValidatorAdapter里),还没有真正创建Validator,
// 这个ValidatorAdapter成为注入到Spring容器的bean,名称是mvcValidator
// 源码位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
@Bean
public Validator mvcValidator() {
    if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
        return super.mvcValidator();
    }
    // 1. 调getValidator()获取Validator
    // 4. getValidator()获取的Validator作为默认的,继续由ValidatorAdapter.get()处理
    // 13. 完成mvcValidator创建,mvcValidator实际是一个包装了OptionalValidatorFactoryBean的ValidatorAdapter
    //     OptionalValidatorFactoryBean的父类LocalValidatorFactoryBean实现了InitializingBean接口会触发afterPropertiesSet()调用
    return ValidatorAdapter.get(getApplicationContext(), getValidator());
}

// 源码位置:org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
public void afterPropertiesSet() {
    Configuration<?> configuration;
    // 14. 如果提供了providerClass,则由providerClass来进行配置,否则加载classpath下services的ValidationProvider
    //     由于上面OptionalValidatorFactoryBean是直接new出来的,这个providerClass也没有赋值
    if (this.providerClass != null) {
        ProviderSpecificBootstrap bootstrap = Validation.byProvider(this.providerClass);
        if (this.validationProviderResolver != null) {
            bootstrap = bootstrap.providerResolver(this.validationProviderResolver);
        }
        configuration = bootstrap.configure();
    }
    else {
        GenericBootstrap bootstrap = Validation.byDefaultProvider();
        if (this.validationProviderResolver != null) {
            bootstrap = bootstrap.providerResolver(this.validationProviderResolver);
        }
        // 这里会加载classpath下services的ValidationProvider,如果引了hibernate-validator包就会有,
        // 如果没有引hibernate-validator包,这里会抛异常结束,即没有创建实际的Validator
        configuration = bootstrap.configure();
    }

    // 省略部分代码
    
    try {
        // 实际的Validator要由validationProvider提供
        this.validatorFactory = configuration.buildValidatorFactory();
        setTargetValidator(this.validatorFactory.getValidator());
    }
    finally {
        closeMappingStreams(mappingStreams);
    }
}

从上面代码看,如果希望mvcValidator里的targetValidator有值,有两个方法:

  • 用WebMvcConfigurer提供一个实现了Validator接口且不是SpringValidatorAdapter子类的validator。
  • 通过Services的方式提供。

2.2 设置mvcValidator

RequestMappingHandlerAdapter是SpringMVC处理http请求的关键类,在初始化的时候设置了WebBindingInitializer,而mvcValidator则设置到了WebBindingInitializer里,通过这种方式把mvcValidator组装到了RequestMappingHandlerAdapter里。

java 复制代码
// 源码位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
        @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
        @Qualifier("mvcConversionService") FormattingConversionService conversionService,
        @Qualifier("mvcValidator") Validator validator) {
    // 1. 初始化RequestMappingHandlerAdapter时,把mvcValidator设置到RequestMappingHandlerAdapter
    RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);
    adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
    return adapter;
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
// 继承关系: EnableWebMvcConfiguration < DelegatingWebMvcConfiguration < WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
        @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
        @Qualifier("mvcConversionService") FormattingConversionService conversionService,
        @Qualifier("mvcValidator") Validator validator) {
    RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    adapter.setContentNegotiationManager(contentNegotiationManager);
    adapter.setMessageConverters(getMessageConverters());
    
    // 2. mvcValidator实际是设置到了WebBindingInitializer,而WebBindingInitializer则设置到了RequestMappingHandlerAdapter里
    adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
    adapter.setCustomArgumentResolvers(getArgumentResolvers());
    adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

    if (jackson2Present) {
        adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
        adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
    }

    AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
    if (configurer.getTaskExecutor() != null) {
        adapter.setTaskExecutor(configurer.getTaskExecutor());
    }
    if (configurer.getTimeout() != null) {
        adapter.setAsyncRequestTimeout(configurer.getTimeout());
    }
    adapter.setCallableInterceptors(configurer.getCallableInterceptors());
    adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

    return adapter;
}

// 源码位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer(FormattingConversionService mvcConversionService, Validator mvcValidator) {
    try {
        return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
    }
    catch (NoSuchBeanDefinitionException ex) {
        // 3. 在父类设置
        return super.getConfigurableWebBindingInitializer(mvcConversionService, mvcValidator);
    }
}

// 源码位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer(FormattingConversionService mvcConversionService, Validator mvcValidator) {
    // 4. 创建WebBindingInitializer(ConfigurableWebBindingInitializer),并设置mvcValidator
    ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
    initializer.setConversionService(mvcConversionService);
    initializer.setValidator(mvcValidator);
    
    MessageCodesResolver messageCodesResolver = getMessageCodesResolver();
    if (messageCodesResolver != null) {
        initializer.setMessageCodesResolver(messageCodesResolver);
    }
    return initializer;
}

2.3 请求参数校验

当发起HTTP请求的时候,SpringMVC会通过RequestMappingHandlerAdapter把mvcValidator设置到DataBinder(ExtendedServletRequestDataBinder)中,在对请求参数处理的时候,如果参数指定了需要校验的注解(如@Valid),则在DataBinder里用Validator对参数进行校验,校验的结果放到BindingResult里。最后判断BindingResult里是否带了错误信息,如果带了则抛异常,如果抛异常则不会进入Controller的接口。

java 复制代码
// BindingResult接口是Errors接口的子接口,它们是Spring的Validation机制的重要概念。
// Errors主要用于存储对象和对象方法的validation错误,前者用reject()接口,后者用rejectValue()接口。
// BindingResult则还记录了产生validation错误的对象
public interface BindingResult extends Errors {
    Object getTarget();
    // 省略其它接口
}
// 源码位置:org.springframework.validation.Errors
public interface Errors {
    void reject(String errorCode);
    void reject(String errorCode, String defaultMessage);
    void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
    void rejectValue(@Nullable String field, String errorCode);
    void rejectValue(@Nullable String field, String errorCode, String defaultMessage);
    void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage);
    // 省略其它接口
}

// RequestMappingHandlerAdapter处理http请求时,在匹配请求参数和Controller接口参数的时候,需要用到各种各样的MethodProcessor,
// 当参数类型属于自定义的类型,匹配到的处理类是ModelAttributeMethodProcessor;
// binderFactory为ServletRequestDataBinderFactory,里面带了ConfigurableWebBindingInitializer,WebBindingInitializer带了mvcValidator;
// binderFactory的ConfigurableWebBindingInitializer由RequestMappingHandlerAdapter提供。
// 源码位置:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    String name = ModelFactory.getNameForParameter(parameter);
    ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
    if (ann != null) {
        mavContainer.setBinding(name, ann.binding());
    }

    Object attribute = null;
    BindingResult bindingResult = null;

    if (mavContainer.containsAttribute(name)) {
        attribute = mavContainer.getModel().get(name);
    }
    else {
        try {
            // 1. 创建参数类型对应的对象,如例子里的GroupMember
            attribute = createAttribute(name, parameter, binderFactory, webRequest);
        }
        catch (BindException ex) {
            if (isBindExceptionRequired(parameter)) {
                throw ex;
            }
            if (parameter.getParameterType() == Optional.class) {
                attribute = Optional.empty();
            }
            else {
                attribute = ex.getTarget();
            }
            bindingResult = ex.getBindingResult();
        }
    }

    if (bindingResult == null) {
        // 2. 创建一个WebDataBinder,由父类DefaultDataBinderFactory提供createBinder()方法
        WebDataBinder binder = binderFactory.createBinder()方法(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                bindRequestParameters(binder, webRequest);
            }
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new BindException(binder.getBindingResult());
            }
        }
        if (!parameter.getParameterType().isInstance(attribute)) {
            attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
        }
        bindingResult = binder.getBindingResult();
    }

    Map<String, Object> bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return attribute;
}

// 源码位置:org.springframework.web.bind.support.DefaultDataBinderFactory
public final WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
    // 3. 调用ServletRequestDataBinderFactory的createBinderInstance()创建
    //    继承关系:ServletRequestDataBinderFactory < InitBinderDataBinderFactory < DefaultDataBinderFactory
    WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    if (this.initializer != null) {
        this.initializer.initBinder(dataBinder, webRequest);
    }
    initBinder(dataBinder, webRequest);
    return dataBinder;
}

// 源码位置:org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory
protected ServletRequestDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest request) throws Exception  {
    // 4. 创建WebDataBinder对象
    return new ExtendedServletRequestDataBinder(target, objectName);
}

// 回到DefaultDataBinderFactory的createBinder()继续处理
// 源码位置:org.springframework.web.bind.support.DefaultDataBinderFactory
public final WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
    // 3. 调用ServletRequestDataBinderFactory的createBinderInstance()创建ExtendedServletRequestDataBinder
    WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
    
    if (this.initializer != null) {
        // 4. 初始化WebDataBinder
        //    initializer为ConfigurableWebBindingInitializer,实现了WebBindingInitializer接口,initBinder()由接口提供
        this.initializer.initBinder(dataBinder, webRequest);
    }
    initBinder(dataBinder, webRequest);
    return dataBinder;
}

// 源码位置:org.springframework.web.bind.support.WebBindingInitializer
default void initBinder(WebDataBinder binder, WebRequest request) {
    // 5. 调用子类方法初始化,子类为ConfigurableWebBindingInitializer
    initBinder(binder);
}

// 源码位置:org.springframework.web.bind.support.ConfigurableWebBindingInitializer
public void initBinder(WebDataBinder binder) {
    binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
    if (this.directFieldAccess) {
        binder.initDirectFieldAccess();
    }
    if (this.messageCodesResolver != null) {
        binder.setMessageCodesResolver(this.messageCodesResolver);
    }
    if (this.bindingErrorProcessor != null) {
        binder.setBindingErrorProcessor(this.bindingErrorProcessor);
    }
    
    // 6. 把mvcValidator设置到WebDataBinder中,mvcValidator实际为SpringValidatorAdapter
    if (this.validator != null && binder.getTarget() != null && this.validator.supports(binder.getTarget().getClass())) {
        binder.setValidator(this.validator);
    }
    if (this.conversionService != null) {
        binder.setConversionService(this.conversionService);
    }
    if (this.propertyEditorRegistrars != null) {
        for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
            propertyEditorRegistrar.registerCustomEditors(binder);
        }
    }
}

// 回到ModelAttributeMethodProcessor继续处理
// 源码位置:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 省略部分代码

    if (bindingResult == null) {
        // 2. 创建一个WebDataBinder,由父类DefaultDataBinderFactory提供createBinder()方法
        WebDataBinder binder = binderFactory.createBinder()方法(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                bindRequestParameters(binder, webRequest);
            }
            
            // 7. 校验参数值
            validateIfApplicable(binder, parameter);
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new BindException(binder.getBindingResult());
            }
        }
        if (!parameter.getParameterType().isInstance(attribute)) {
            attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
        }
        bindingResult = binder.getBindingResult();
    }

    Map<String, Object> bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return attribute;
}

// 源码位置:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    for (Annotation ann : parameter.getParameterAnnotations()) {
        // 8. 把参数前指定的注解取出来,确定哪些是和validation有关的
        Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
        if (validationHints != null) {
            binder.validate(validationHints);
            break;
        }
    }
}

// 9. 一般要校验的参数前面要加上@Valid注解
//    从下面代码看不止这一种方式,还可以指定有值的@Validated注解,也可以自定义以Valid开头的注解等
// 源码位置:org.springframework.validation.annotation.ValidationAnnotationUtils
public static Object[] determineValidationHints(Annotation ann) {
    // 指定了注解@Validated
    if (ann instanceof Validated) {
        return ((Validated) ann).value();
    }
    
    // 指定了注解@Valid
    Class<? extends Annotation> annotationType = ann.annotationType();
    if ("javax.validation.Valid".equals(annotationType.getName())) {
        return EMPTY_OBJECT_ARRAY;
    }
    
    // 注解间接指定了注解@Validated
    Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
    if (validatedAnn != null) {
        return validatedAnn.value();
    }
    // 自定义注解是以Valid开头命名的
    if (annotationType.getSimpleName().startsWith("Valid")) {
        return convertValidationHints(AnnotationUtils.getValue(ann));
    }
    
    return null;
}

// 回到ModelAttributeMethodProcessor的validateIfApplicable()继续处理
// 源码位置:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
    for (Annotation ann : parameter.getParameterAnnotations()) {
        // 8. 把参数前指定的注解取出来,确定哪些是和validation有关的
        Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
        if (validationHints != null) {
            // 如果匹配到了注解,则进行校验
            binder.validate(validationHints);
            break;
        }
    }
}

// 源码位置:org.springframework.validation.DataBinder
public void validate(Object... validationHints) {
    Object target = getTarget();
    // 记录校验错误的BindingResult,一般为BeanPropertyBindingResult
    BindingResult bindingResult = getBindingResult();
    
    // 9. 获取到所有的Validator来校验,从前面看基本只有一个
    for (Validator validator : getValidators()) {
        if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
            ((SmartValidator) validator).validate(target, bindingResult, validationHints);
        }
        else if (validator != null) {
            // 10. 调用Validator的validate()方法进行校验,校验的结果要放到bindingResult中
            validator.validate(target, bindingResult);
        }
    }
}

// 源码位置:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // 省略部分代码

    if (bindingResult == null) {
        // 2. 创建一个WebDataBinder,由父类DefaultDataBinderFactory提供createBinder()方法
        WebDataBinder binder = binderFactory.createBinder()方法(webRequest, attribute, name);
        if (binder.getTarget() != null) {
            if (!mavContainer.isBindingDisabled(name)) {
                bindRequestParameters(binder, webRequest);
            }
            
            // 7. 校验参数值
            validateIfApplicable(binder, parameter);
            
            // 11. 如果有validation错误就抛异常
            if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                throw new BindException(binder.getBindingResult());
            }
        }
        if (!parameter.getParameterType().isInstance(attribute)) {
            attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
        }
        bindingResult = binder.getBindingResult();
    }

    Map<String, Object> bindingResultModel = bindingResult.getModel();
    mavContainer.removeAttributes(bindingResultModel);
    mavContainer.addAllAttributes(bindingResultModel);

    return attribute;
}

2.4 Validation的使用

Spring提供的Validation模型为:

模型的核心部分是:

  • Valid和Validated注解:主要是用来识别是否需要进行校验,如果不加这些注解,那么就相当于没有开启校验。注解由参数MethodParameter提供。
  • Validator:主要实现校验逻辑。supports()用于识别参数的类型,validate()则完成具体的校验逻辑。
  • Errors:用于存储校验的错误结果,分为整个对象的错误(如对象为null)和属性错误。ModelAttributeMethodProcessor等会从它里面获取错误进行处理。
  • WebDataBinder用于组合Validator和Errors,完成整体的校验。
    从上面看,要使用Spring提供的Validation对参数校验需要:
  1. 自定义一个校验类,实现org.springframework.validation.Validator接口:
java 复制代码
public interface Validator {
    boolean supports(Class<?> clazz);
    void validate(Object target, Errors errors);
}
  1. 把这个自定义类加到WebMvcConfigurer中
java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public Validator getValidator() {
        return new CustomValidator();
    }
}
  1. 在Validator的validate()校验到错误的时候,错误信息要加到Errors中

在support()里要定义好HTTP请求参数类型的匹配规则,这个方法的参数是HTTP请求参数对应的类。最直接的方式就是列出所有要支持校验的类,但这样扩展性就比较差,如果要扩展成一个框架,就得用其它方式,比如定义一些注解,通过这个类型来获取是否指定了注解,然后根据注解类做事情。

同理,validate()工作如果不是硬编码也不太容易。所以Spring提供的Validation有点比较原始,不方便直接使用。

3 架构一小步

使用Spring提供的@Valid注解标识Controller接口里接收的参数,增加扩展来支持校验(这个扩展有现成的hibernate-validator包)。

相关推荐
_Aaron___17 分钟前
面向对象的三大特性---多态
java
Kiri霧23 分钟前
IntelliJ IDEA
java·ide·kotlin·intellij-idea
daixin884844 分钟前
什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
java·开发语言·redis·缓存
京茶吉鹿1 小时前
"if else" 堆成山?这招让你的代码优雅起飞!
java·后端
你我约定有三1 小时前
RabbitMQ--消息丢失问题及解决
java·开发语言·分布式·后端·rabbitmq·ruby
张北北.1 小时前
【深入底层】C++开发简历4+4技能描述6
java·开发语言·c++
Java初学者小白1 小时前
秋招Day19 - 分布式 - 分布式事务
java·分布式
rannn_1112 小时前
Java学习|黑马笔记|Day23】网络编程、反射、动态代理
java·笔记·后端·学习
火车叨位去19492 小时前
用Java实现rpc的逻辑和流程图和核心技术与难点分析
java·rpc·流程图
五点六六六2 小时前
前端常见的性能指标采集
前端·性能优化·架构