1 概述
對于Web服務,需要對請求的參數進行校驗,可以對不合法的參數進行提示,提高用戶體驗。也可以防止有人惡意用一些非法的參數對網站造成破壞。如果是對每個參數都寫一段代碼來判斷值是否合法,那校驗的代碼就很多,也很繁瑣。Spring提供了一套校驗機制,先來了解一下。
2 原理
2.1 初始化mvcValidator
初始化mvcValidator,mvcValidator是一個SpringValidatorAdapter,其里面的targetValidator是真正執行validate校驗的對象,初始化mvcValidator的主要目的就是要把targetValidator組裝好。
// 源碼位置: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,此時沒有定義返回的是nullValidator 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. 先獲取已經存在的ValidatorValidator 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默認沒有注入實現了該接口的beanjavax.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. 先獲取已經存在的ValidatorValidator 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.ValidatorOptionalValidatorFactoryBean 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的一個工廠beanOptionalValidatorFactoryBean 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而直接作為mvcValidatorreturn 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包,這里會拋異常結束,即沒有創建實際的Validatorconfiguration = bootstrap.configure();}// 省略部分代碼try {// 實際的Validator要由validationProvider提供this.validatorFactory = configuration.buildValidatorFactory();setTargetValidator(this.validatorFactory.getValidator());}finally {closeMappingStreams(mappingStreams);}
}
- 用WebMvcConfigurer提供一個實現了Validator接口且不是SpringValidatorAdapter子類的validator。
- 通過Services的方式提供。
2.2 設置mvcValidator
// 源碼位置: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設置到RequestMappingHandlerAdapterRequestMappingHandlerAdapter 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),并設置mvcValidatorConfigurableWebBindingInitializer 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的接口。
// 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. 創建參數類型對應的對象,如例子里的GroupMemberattribute = 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 < DefaultDataBinderFactoryWebDataBinder 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()創建ExtendedServletRequestDataBinderWebDataBinder 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. 調用子類方法初始化,子類為ConfigurableWebBindingInitializerinitBinder(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實際為SpringValidatorAdapterif (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) {// 指定了注解@Validatedif (ann instanceof Validated) {return ((Validated) ann).value();}// 指定了注解@ValidClass<? extends Annotation> annotationType = ann.annotationType();if ("javax.validation.Valid".equals(annotationType.getName())) {return EMPTY_OBJECT_ARRAY;}// 注解間接指定了注解@ValidatedValidated 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,一般為BeanPropertyBindingResultBindingResult 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的使用

- Valid和Validated注解:主要是用來識別是否需要進行校驗,如果不加這些注解,那么就相當于沒有開啟校驗。注解由參數MethodParameter提供。
- Validator:主要實現校驗邏輯。supports()用于識別參數的類型,validate()則完成具體的校驗邏輯。
- Errors:用于存儲校驗的錯誤結果,分為整個對象的錯誤(如對象為null)和屬性錯誤。ModelAttributeMethodProcessor等會從它里面獲取錯誤進行處理。
- WebDataBinder用于組合Validator和Errors,完成整體的校驗。
public interface Validator {boolean supports(Class<?> clazz);void validate(Object target, Errors errors);
}
2) 把這個自定義類加到WebMvcConfigurer中
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic Validator getValidator() {return new CustomValidator();}
}
3) 在Validator的validate()校驗到錯誤的時候,錯誤信息要加到Errors中
在support()里要定義好HTTP請求參數類型的匹配規則,這個方法的參數是HTTP請求參數對應的類。最直接的方式就是列出所有要支持校驗的類,但這樣擴展性就比較差,如果要擴展成一個框架,就得用其它方式,比如定義一些注解,通過這個類型來獲取是否指定了注解,然后根據注解類做事情。
同理,validate()工作如果不是硬編碼也不太容易。所以Spring提供的Validation有點比較原始,不方便直接使用。
3 架構一小步
使用Spring提供的@Valid注解標識Controller接口里接收的參數,增加擴展來支持校驗(這個擴展有現成的hibernate-validator包)。