本系列文章的上一篇 : Spring MVC : 控制器方法处理请求的过程分析 - 2. 请求参数的获取
在本系列文章的上一篇中,我们分析了请求参数获取的主流程。经过该主流程,从请求上下文中分析出了控制器方法执行所需的参数值列表,但该过程中非常关键的一个环节,也就是通过this.resolvers
解析每个参数值的详情,我们并没有展开,到目前来讲,这一步对我们来讲仍然是黑盒子。在这篇文章中,我们就详细分析这一步骤。
提到通过this.resolvers
解析参数,我们绕不开接口HandlerMethodArgumentResolver
。该接口是控制器方法参数解析器的概念建模接口。该接口定义了两个方法,分别定义了一个控制器方法参数解析器所要具备的两个核心能力:
-
boolean supportsParameter(MethodParameter parameter)
告诉调用者自己是否支持该参数的解析。该方法的实现通常基于参数的特征来声明自己是否支持。这些特征比如是参数的注解或者类型。调用者如果想使用某个
HandlerMethodArgumentResolver
对象解析参数,通常需要先调用方法supportsParameter
检测它是否支持解析该参数。supportsParameter
接收1个参数 :parameter
– 表示要被检测是否支持的目标方法参数
通过parameter
,可以获得该参数在参数列表中的索引位置,类型,该参数上的各种注解信息等等。
-
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
解析指定参数的参数值。通常调用者首先调用某个
HandlerMethodArgumentResolver
对象的方法supportsParameter
询问它是否可解析该参数,如果可以的话,然后调用其方法resolveArgument
尝试解析参数值。我们知道,来自请求的信息都是字节形式存在的,即使通过指定的字符集转换成了字符串,然后通过类似request.getParameterValues
这样的方法获取得到参数值,此时参数值也是字符串或者字符串数组类型的,而通过方法HandlerMethodArgumentResolver#resolveArgument
的解析,我们得到的参数值将会是已经被翻译成目标参数类型了。这也正是我们可以把这一步骤叫做"参数值绑定"的原因。resolveArgument
方法接收4个参数 :parameter
– 将要解析参数值的目标方法参数信息:参数索引,参数类型,参数注解(名称,缺省值,是否必要)等信息都包含在这里mavContainer
– 解析过程逻辑会做一些决定,这些决定信息记录在该对象中webRequest
– 当前用户请求及相关信息,也就是解析参数值的请求上下文binderFactory
– 用于生成数据绑定器的工厂对象,一个数据绑定器用于将某个值从一个源类型转换成一个目标类型
从方法
resolveArgument
的签名我们不难推断,它会针对parameter
从请求上下文webRequest
分析出参数值,使用binderFactory
生成的数据绑定器进行类型转换,最终返回类型匹配可直接应用的参数值。-- 事实上正是如此,我们继续分析。
ServletInvocableHandlerMethod/InvocableHandlerMethod
的实例成员属性resolvers
类型为HandlerMethodArgumentResolverComposite
它就实现了接口HandlerMethodArgumentResolver
,具备HandlerMethodArgumentResolver
约定的方法参数解析的能力。但实际上,HandlerMethodArgumentResolverComposite
并不自己解析参数,它只是组合模式的一个应用,它组合了多个HandlerMethodArgumentResolver
,当有任务到达时,HandlerMethodArgumentResolverComposite
会将任务继续委托所组合的某个HandlerMethodArgumentResolver
来完成。
了解完HandlerMethodArgumentResolverComposite
的工作原理之后,我们需要继续了解每个真正执行参数解析和绑定任务的HandlerMethodArgumentResolver
是如何工作的,因为只有了解它们的工作原理,我们才算全面掌握控制器方法参数值解析和绑定这一过程。
但通过一篇文章讲解所有HandlerMethodArgumentResolver
实现类并不现实,因为Spring Web/Web MVC
内置实现了二十多个HandlerMethodArgumentResolver
类,分别负责从请求上下文中的各个方面解析和绑定参数值。所以本文这里不再详细展开,具体每个HandlerMethodArgumentResolver
实现类是如何工作的,可以阅读如下文章 :
读到这里,你可能会有这样一个问题 :仅仅是一个参数解析器,为什么Spring
会提供这么多实现类呢 ?这里,我们必须要分析一下对"参数"这一概念的理解了。通过该分析,你应该会明白Spring
为什么要这么做。
一般意义上我们所说的请求参数,通常是如下几类 :
GET
请求时,URL QueryString
中传递的参数POST
请求时,通过表单传递的参数- 通过
URL
传递路径变量
以上参数,可以认为是狭义上的请求参数。但广义上来讲,跟当前请求相关的信息中可变的部分,比如URL
,QueryString
,请求头部,会话,Cookie
,甚至请求体(request body
,request payload
)都可以作为参数的来源,不仅如此,参数也可以不仅仅是简单数据类型,也可以是结构化的,比如JSON
格式,矩阵格式等等,这个层面的参数概念,可以理解成广义的请求参数。正是因此,Spring Web
才提供了各种类型的HandlerMethodArgumentResolver
实现类来支持这些多种多样的请求参数的解析。而至于Spring Web/Web MVC
提供了多少HandlerMethodArgumentResolver
实现类,可以参考文章:Spring MVC : 框架缺省的控制器方法参数解析器
总结 :
Spring Web
从请求上下文中获取参数值到目标控制器方法的绑定是通过HandlerMethodArgumentResolver
来完成的。- 一个
HandlerMethodArgumentResolver
解析请求参数主要完成以下任务 :- 从请求上下文中获取参数值,通常是字符串类型,或是
null
; - 根据目标方法上对该请求参数的缺省值信息确定是否要应用缺省值;
- 结合参数值和参数必要性定义做相应的逻辑处理;
- 绑定 : 将通常源类型为字符串的参数值转换成目标控制器方法参数类型的参数值;
- 从请求上下文中获取参数值,通常是字符串类型,或是
Spring Web
提供了多种HandlerMethodArgumentResolver
实现类来解析和绑定各种类型的请求参数。
本系列文章链接合集 :