前面我们分析了,如果我们自己要实现 spring mvc 框架的话,大致需要实现如下功能:
- 0、将 url 与 Controller method 的对应关系进行注册
- 1、通过请求的 url 找到 Controller method (即 url 与 Controller method 的映射)
- 2、将请求参数进行绑定,即将入参绑定到 Controller method 的参数对象上
- 3、执行处 Controller method (即 HandlerAdapter#handle())
- 4、对 Controller method 的返回值进行处理
4.1 如果正常返回的话,对返回值对象进行处理(即 ReturnValueHandler)
包括:如果返回视图 View 的话,对视图进行渲染 (即 ViewResolver)
4.2 如果有异常返回的话,对异常进行处理(即 @ExceptionHandler)
上一篇我们分析了根据 url 如何找到 Controller 方法,接下来,我们再看分析一下请求参数是如何绑定到 handler method 的参数对象上的?
handler method 参数绑定的入口
还是从 DispatcherServlet 出发,来看下 Spring MVC 是在哪里开始进行参数绑定的?
可以看到,DispatcherServlet#doDispatch()
的处理大体分了 6 步:
1、获取 request 对应的 HandlerExecutionChain(它包含 handler 和 interceptors)
2、获取 handler 对应的 HandlerAdapter
3、执行 handler 的前置处理: HandlerInterceptor#preHandle()
4、通过 HandlerAdapter#handle()
来执行处理程序
5、执行 handler 的后置处理:HandlerInterceptor#postHandle()
6、对返回值进行处理(包括正常返回 和 异常处理)
所以,参数绑定应该是在执行 handler method 的时候,即 HandlerAdapter#handler()
。
- 为什么需要获取
HandlerAdapter
之后,通过HandlerAdapter#handler()
来执行 handler method 呢?
答: 因为 Spring MVC 支持多种 handler,比如:@RequestMapping 定义的 handler method、通过org.springframework.web.servlet.mvc.Controller
接口定义的 handler method、servlet 方式定义的 handler method等。
为了让这些不同种类的 handler method 可以通过统一的方式进行调用执行,Spring MVC 抽象出了HandlerAdapter
接口来统一处理客户端的 request 请求。
RequestMappingHandlerAdapter
SpringMVC 中最常用的是 @RequestMapping 标记 handler method。
@RequestMapping 标记的 HandlerMethod 对应的 HandlerAdapter 是 RequestMappingHandlerAdapter
。
RequestMappingHandlerAdapter#handle()
具体的调用过程如下:
可以看到,参数解析、handler method 的执行 和 对返回值的处理,最终是通过 ServletInvocableHandlerMethod#invokeAndHandle()
来处理的。
ServletInvocableHandlerMethod#invokeAndHandle()
的处理过程如下:
可以看到,参数的解析绑定是通过 HandlerMethodArgumentResolver 来处理的。
HandlerMethodArgumentResolver
HandlerMethodArgumentResolver
是 SpringMVC 用来将 request 中的参数解析出来赋值给 handler method 的入参的。
HandlerMethodArgumentResolver 的类图如下:(精简了一些实现类)
从上图可以看到 HandlerMethodArgumentResolver
的子类针对 Map、Model、@RequestParam、@PathVariable、@RequestBody、ServletRequest、HttpSession、TimeZone等都分别进行了解析处理。
我们重点看下对 @RequestBody 参数的处理:
@RequestBody 标记的参数,最终会通过 HttpMessageConverter#read()
来进行处理。
在 SpringBoot 环境下,默认会使用 MappingJackson2HttpMessageConverter
来处理。
小结
SpringMVC 的请求参数解析通常是通过 HandlerMethodArgumentResolver
来解析的,它会解析 request 中的参数,并赋值给 handler method 入参。HandlerMethodArgumentResolver
的子类针对 Map、Model、@RequestParam、@PathVariable、@RequestBody、ServletRequest、HttpSession、TimeZone等都分别进行了解析处理。