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对参数校验需要:
- 自定义一个校验类,实现org.springframework.validation.Validator接口:
java
public interface Validator {
boolean supports(Class<?> clazz);
void validate(Object target, Errors errors);
}
- 把这个自定义类加到WebMvcConfigurer中
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public Validator getValidator() {
return new CustomValidator();
}
}
- 在Validator的validate()校验到错误的时候,错误信息要加到Errors中
在support()里要定义好HTTP请求参数类型的匹配规则,这个方法的参数是HTTP请求参数对应的类。最直接的方式就是列出所有要支持校验的类,但这样扩展性就比较差,如果要扩展成一个框架,就得用其它方式,比如定义一些注解,通过这个类型来获取是否指定了注解,然后根据注解类做事情。
同理,validate()工作如果不是硬编码也不太容易。所以Spring提供的Validation有点比较原始,不方便直接使用。
3 架构一小步
使用Spring提供的@Valid注解标识Controller接口里接收的参数,增加扩展来支持校验(这个扩展有现成的hibernate-validator包)。