总结摘要
SpringMVC HandlerMethodArgumentResolver Detail
参数校验异常
request 请求参数解析可能抛出 3 种常见异常
- MethodArgumentNotValidException
- 场景:
@RequestBody修饰的入参校验 - 处理器:RequestResponseBodyMethodProcessor
- BindException
- 场景:未使用
@RequestBody或@RequestParam等注解修饰的入参校验,且入参对象被@Valid或 @Validated相关注解修饰。 - 处理器:ServletModelAttributeMethodProcessor/ModelAttributeMethodProcessor
- ConstraintViolationException
- 场景:类被
@Validated注解修饰,且未使用 @RequestBody修饰入参对象,且入参对象被@Constraint相关注解修饰。 - 处理器:org.springframework.validation.beanvalidation.MethodValidationInterceptor
被@Validated注解修饰的类的方法均会由 MethodValidationInterceptor 拦截器校验参数,优先级低于请求参数处理器(例如 RequestResponseBodyMethodProcessor)。
RequestResponseBodyMethodProcessor
处理 @RequestBody 或 @ResponseBody 注解修饰参数的方法参数处理器。
1
| // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
|

流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| RequestResponseBodyMethodProcessor#resolveArgument
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#resolveArgument
-> 反序列化对象,暂时略。org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#readWithMessageConverters
-> 获取参数的名称,可能添加标识类型的名称后缀。org.springframework.core.Conventions#getVariableNameForParameter
-> 创建 WebDataBinder。org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
-> 创建 DataBinder 实例。org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory#createBinderInstance
-> new ExtendedServletRequestDataBinder
-> 初始化实例。org.springframework.web.bind.support.WebBindingInitializer#initBinder(org.springframework.web.bind.WebDataBinder, org.springframework.web.context.request.WebRequest)
-> 配置属性。org.springframework.web.bind.support.ConfigurableWebBindingInitializer#initBinder
-> debug 时实际配置的属性有 validator,conversionService,此时不包括 propertyEditorRegistrars。
-> 如果 arg 不为空
-> 校验属性。org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#validateIfApplicable
-> 获取参数的注解。parameter.getParameterAnnotations()
-> for-each annotations
-> 校验是否属于 Spring's Validated, @javax.validation.Valid,以及扩展点以“Valid”名称起始的主键。org.springframework.validation.annotation.ValidationAnnotationUtils#determineValidationHints。
-> 如果是有效注解,则校验注解。org.springframework.validation.DataBinder#validate(java.lang.Object...)
-> org.springframework.validation.DataBinder#getBindingResult
-> org.springframework.validation.DataBinder#getInternalBindingResult。可能使用 DirectFieldBindingResult 或 BeanPropertyBindingResult 两种类型。
-> for-each this.validators
-> 如果是 SmartValidator 实例,则调用 org.springframework.validation.SmartValidator#validate
-> 否则调用 org.springframework.validation.Validator#validate。
-> 如果存在未通过的校验规则,则抛出异常 MethodArgumentNotValidException。
-> 将校验结果存储在 mavContainer,key 是 BindingResult.MODEL_KEY_PREFIX + name。
-> 适配参数为 Optional 的场景,返回 Optional.empty() 或 Optional.of(arg)。org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver#adaptArgumentIfNecessary
|
RequestParamMethodArgumentResolver
1
2
3
4
5
| Supports the following:
-> @RequestParam-annotated method arguments. This excludes Map params where the annotation does not specify a name. See RequestParamMapMethodArgumentResolver instead for such params.
-> Arguments of type MultipartFile unless annotated with @RequestPart.
-> Arguments of type Part unless annotated with @RequestPart.
-> In default resolution mode, simple type arguments even if not with @RequestParam.
|
1
| // org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
|

流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| -> org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
-> 获取 NamedValueInfo。org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#getNamedValueInfo
-> 首先从缓存 this.namedValueInfoCache 中获取。
-> 如果缓存中为空,则创建。
-> 获取参数中的注解 @RequestParam,将注解封装为 RequestParamNamedValueInfo。org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#createNamedValueInfo
-> 使用上一步骤获取的信息,重新封装为 NamedValueInfo。org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#updateNamedValueInfo
-> 如果 @RequestParam 注解的 name 属性为空,则使用形参的名称。
-> new NamedValueInfo
-> 存储到缓存 this.namedValueInfoCache
-> 适配 Optional,从中获取真实类型。
-> 解析参数名称中的表达式(例如占位符等),详情略。org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveEmbeddedValuesAndExpressions
-> 根据名称从 request 中解析参数值。org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName
-> 如果上一步骤未获取到值,则或使用默认值,或抛出异常,或返回空值。如果上一步骤获取到的值为空字符,且默认值不为空,则使用默认值。
-> 创建 WebDataBinder。org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
-> 创建 DataBinder 实例。org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory#createBinderInstance
-> new ExtendedServletRequestDataBinder
-> 初始化实例。org.springframework.web.bind.support.WebBindingInitializer#initBinder(org.springframework.web.bind.WebDataBinder, org.springframework.web.context.request.WebRequest)
-> 配置属性。org.springframework.web.bind.support.ConfigurableWebBindingInitializer#initBinder
-> debug 时实际配置的属性有 validator,conversionService,此时不包括 propertyEditorRegistrars。
-> 类型转换。org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // 类型转换。org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
-> 获取 TypeConverter。org.springframework.validation.DataBinder#getTypeConverter
-> if
-> else 解析简单类型参数,返回 SimpleTypeConverter。org.springframework.validation.DataBinder#getSimpleTypeConverter
-> 创建 SimpleTypeConverter,并配置属性。org.springframework.beans.SimpleTypeConverter#SimpleTypeConverter
-> this.typeConverterDelegate = new TypeConverterDelegate。this.typeConverterDelegate.propertyEditorRegistry 设置为当前创建的 SimpleTypeConverter。
-> this.defaultEditorsActive = true;
-> 配置 conversionService
-> org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
-> org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)
-> org.springframework.beans.TypeConverterDelegate#convertIfNecessary(java.lang.String, java.lang.Object, java.lang.Object, java.lang.Class<T>, org.springframework.core.convert.TypeDescriptor)
-> 根据参数名称或参数类型查找自定义类型转换器。org.springframework.beans.PropertyEditorRegistrySupport#findCustomEditor
-> 使用 ConversionService 转换参数。
-> 是否支持转换。org.springframework.core.convert.support.GenericConversionService#canConvert(org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
-> 如果存在匹配的转换器,则实际进行转换并 return。org.springframework.core.convert.support.GenericConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
-> 如果存在自定义转换器,或上一步骤转换结果的类型与期望类型不匹配,则进一步转换。
-> 如果不存在自定义转换器,则获取默认的 PropertyEditor。org.springframework.beans.TypeConverterDelegate#findDefaultEditor
-> 使用转换器转换。org.springframework.beans.TypeConverterDelegate#doConvertValue
-> 进一步类型适配,包括数组、集合、Optional 值等。
|
1
2
3
4
5
| org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
org.springframework.beans.TypeConverterSupport#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
|
ServletModelAttributeMethodProcessor
解析带有 @ModelAttribute 注解的方法参数并处理带有 @ModelAttribute 注解的方法返回值。
当此处理器以 annotationNotRequired=true 创建时,任何非简单类型的参数和返回值都被视为模型属性,无论是否存在 @ModelAttribute 注解。
1
2
| // org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
// org.springframework.web.method.annotation.ModelAttributeMethodProcessor
|
作为兜底处理器之一。
1
2
3
4
5
6
7
8
| // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultArgumentResolvers
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
//...
// Catch-all
//...
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
|

1
2
3
4
5
6
7
8
9
10
11
12
13
| // org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
-> 解析 @ModelAttribute 注解。parameter.getParameterAnnotation(ModelAttribute.class)。
-> 创建属性。org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#createAttribute
-> 首先根据名称直接从 request 中查询。org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#getRequestValueForAttribute
-> 如果查询到结果,则封装为属性。org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#createAttributeFromRequestValue
-> 如果未查询到结果,场景可能是属性名称为 handler 入参对象的属性,则创建入参对象的实例。org.springframework.web.method.annotation.ModelAttributeMethodProcessor#createAttribute。
-> 创建 WebDataBinder。org.springframework.web.bind.support.DefaultDataBinderFactory#createBinder
-> if (binder.getTarget() != null),即参数类型是 bean 类型而非简单类型。
-> 解析参数。org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#bindRequestParameters
-> 进行参数绑定。org.springframework.web.bind.ServletRequestDataBinder#bind
-> 参数校验。org.springframework.web.method.annotation.ModelAttributeMethodProcessor#validateIfApplicable
-> 如果参数解析失败(绑定失败),则抛出异常 throw new BindException。
-> 如果参数类型与预期不匹配,则进行类型转换。org.springframework.validation.DataBinder#convertIfNecessary(java.lang.Object, java.lang.Class<T>, org.springframework.core.MethodParameter)
|
PathVariableMethodArgumentResolver
处理@PathVariable注解的方法参数处理器。
1
| // org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
|

1
2
3
4
5
| // org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
// 逻辑与 RequestParamMethodArgumentResolver 一致,详情参考 RequestParamMethodArgumentResolver 章节。
// 补充信息,如下方法在 PathVariableMethodArgumentResolver 中重写。
-> 从请求 url 中解析路径参数。org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#resolveName
|
补充信息
BinderFactory
默认使用 ConversionService。

END