Spring源码-SpringMVC-请求参数解析处理器详解

总结摘要
SpringMVC HandlerMethodArgumentResolver Detail

参数校验异常

request 请求参数解析可能抛出 3 种常见异常

  1. MethodArgumentNotValidException
    1. 场景:@RequestBody修饰的入参校验
    2. 处理器:RequestResponseBodyMethodProcessor
  2. BindException
    1. 场景:未使用 @RequestBody@RequestParam等注解修饰的入参校验,且入参对象被@Valid@Validated相关注解修饰。
    2. 处理器:ServletModelAttributeMethodProcessor/ModelAttributeMethodProcessor
  3. ConstraintViolationException
    1. 场景:类被@Validated注解修饰,且未使用 @RequestBody修饰入参对象,且入参对象被@Constraint相关注解修饰。
    2. 处理器: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
-> 创建 WebDataBinderorg.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 时实际配置的属性有 validatorconversionService此时不包括 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
    -> 将校验结果存储在 mavContainerkey  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
    -> 获取 NamedValueInfoorg.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#getNamedValueInfo
        -> 首先从缓存 this.namedValueInfoCache 中获取
        -> 如果缓存中为空则创建
            -> 获取参数中的注解 @RequestParam将注解封装为 RequestParamNamedValueInfoorg.springframework.web.method.annotation.RequestParamMethodArgumentResolver#createNamedValueInfo
            -> 使用上一步骤获取的信息重新封装为 NamedValueInfoorg.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
    -> 如果上一步骤未获取到值则或使用默认值或抛出异常或返回空值如果上一步骤获取到的值为空字符且默认值不为空则使用默认值
    -> 创建 WebDataBinderorg.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 时实际配置的属性有 validatorconversionService此时不包括 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)
-> 获取 TypeConverterorg.springframework.validation.DataBinder#getTypeConverter
    -> if 
    -> else 解析简单类型参数返回 SimpleTypeConverterorg.springframework.validation.DataBinder#getSimpleTypeConverter
        -> 创建 SimpleTypeConverter并配置属性org.springframework.beans.SimpleTypeConverter#SimpleTypeConverter
            -> this.typeConverterDelegate = new TypeConverterDelegatethis.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)
                -> 如果存在匹配的转换器则实际进行转换并 returnorg.springframework.core.convert.support.GenericConversionService#convert(java.lang.Object, org.springframework.core.convert.TypeDescriptor, org.springframework.core.convert.TypeDescriptor)
            -> 如果存在自定义转换器或上一步骤转换结果的类型与期望类型不匹配则进一步转换
                -> 如果不存在自定义转换器则获取默认的 PropertyEditororg.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
-> 创建 WebDataBinderorg.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