前面分析到 SpringMVC工作原理之处理映射[HandlerMapping] ,由映射处理器(HandlerMapping) 解析出对应的 handler。接着 SpringMVC工作原理之适配器[HandlerAdapter] 描述了 handler 是怎么匹配到合适的适配器,进行 handler 对应方法的执行。其他几种适配器还好,但是 RequestMappingHandlerAdapter 适配器对应接下来的参数解析及绑定并执行并不是那么简单,因此本篇笔记主要分析 RequestMappingHandlerAdapter 适配器解析对应 handler 的执行流程。
本篇笔记主要分析SpringMVC 5.1.1 这个版本。
SpringMVC运行流程
RequestMappingHandlerAdapter 大概解析流程如下
RequestMappingHandlerAdapter解析流程
1 了解在前面
在开始下面的具体源码分析前,我们需要了解一些相关的类和接口
1.1 HandlerMethod
在开始记录方法执行流程前,必须要先说下记录方法的对象 HandlerMethod,HandlerMethod 及子类主要用于封装方法调用相关信息。简单理解为保持方法信息的 pojo 类。
HandlerMethod及其子类.png
分析下各个类的功能及职责:
- HandlerMethod 封装方法定义相关的信息 (如类、方法、参数等)
- InvocableHandlerMethod 参数准备委托 HandlerMethodArgumentResolver 进行具体的解析
- ServletInvocableHandlerMethod 添加返回值处理职责,ResponseStatus 处理
在容器初始化的时候,RequestMappingHandlerMapping 映射处理器就将 @RequestMapping 描述的方法以 RequestMappingInfo 为 key,HandlerMethod 为 value 放进自己的缓存 。至如 HandlerMethod 内部后面是怎么进行对应方法上的参数解析及绑定到后来的方法执行等等,咱们接下来会详细讲解。
1.2 参数解析器(HandlerMethodArgumentResolver)和返回值的解析器(HandlerMethodReturnValueHandler)
在分析源码之前,首先让我们来看下SpringMVC中两个重要的接口,两个接口都是在 3.1 版本后添加的。
- 处理方法参数的解析器接口
- 处理方法调用返回值的解析器接口
两个接口分别有两个方法,一个用来查看该解析器是否支持该参数的解析,第二个方法用来对参数进行解析。
1.3 默认解析器的注入
在容器初始话的时候,初始化 RequestMappingHandlerAdapter 适配器的时候会将默认的参数解析器都注入进缓存中。
加载默认的参数解析器(ArgumentResolvers),绑定到 RequestMappingHandlerAdapter 适配器的 argumentResolvers 属性上。
加载默认的返回值解析器(ReturnValueHandlers),绑定到 RequestMappingHandlerAdapter 适配器的 returnValueHandlers 属性上。
下面我们来简单的看下都有哪些默认解析器
- 默认注入的参数解析器
- 默认注入的返回值解析器
2 解析过程流程
2.1 解析器的绑定及匹配
接着 RequestMappingHandlerAdapter 适配器的 handleInternal(..) 方法往下说,在 handleInternal(..) 方法中主要检查是否需要同步执行接下来对方法的操作,内部调用 invokeHandlerMethod(..) 方法。
该方法内部就方法执行流程大致可以分为以上标注的 6 步:
①. 对应 WebDataBinderFactory 工厂类的创建,因里面涉及到的东西有点多,将放在下面参数值类型转换部分详细解说。
②. 根据该 HandlerMethod 创建对应的 ServletInvocableHandlerMethod 对象。
③. 将注入到缓存的参数解析器绑定到创建的 ServletInvocableHandlerMethod 对象上。
④. 将注入缓存的返回值解析器绑定到创建的 ServletInvocableHandlerMethod 对象上。
⑤. 将上面创建的 WebDataBinderFactory 工厂类对象绑定到创建的 ServletInvocableHandlerMethod 对象上。
⑥. 执行 ServletInvocableHandlerMethod 的 invokeAndHandle(...) 方法。
总结:RequestMappingHandlerAdapter 在内部对于每个请求,都会实例化一个 ServletInvocableHandlerMethod 进行处理。
ServletInvocableHandlerMethod 内部会分别对请求跟响应进行处理。
进入执行请求对应方法里面看看流程
接下来就是请求参数解析器和返回值解析器上场的时候了。
①. ServletInvocableHandlerMethod 类在处理参数的时候,会使用自己绑定的参数解析器,参数解析器记录在属性argumentResolvers (这个属性是它的父类 InvocableHandlerMethod中定义的),argumentResolvers 属性是一个 HandlerMethodArgumentResolverComposite 类(这里使用了组合模式的一种变形),这个类是实现了 HandlerMethodArgumentResolver 接口的类,实现了该类里面的两个接口。同时里面有记录所有参数解析器的 List 集合,有缓存 MethodParameter 与解析器对应关系的 Map 集合。
②. ServletInvocableHandlerMethod 类在处理返回值的时候,会使用自身绑定的返回值解析器,该解析器记录在属性 returnValueHandlers (自身属性),returnValueHandlers 属性是一个 HandlerMethodReturnValueHandlerComposite 类(这里使用了组合模式的一种变形),这个类实现了 HandlerMethodReturnValueHandler 接口,实现了该接口里面的两个方法。同时里面有记录所有返回值解析器的 List 集合。
2.2 参数解析器内部解析流程
因为解析器太多,这里只能抽其中一个来了解下参数解析器内部实现解析的逻辑,选个最常用的解析器 RequestParamMethodArgumentResolver,他是用来解析 @RequestParam 注解的参数。RequestParamMethodArgumentResolver 继承自 AbstractNamedValueMethodArgumentResolver,而 AbstractNamedValueMethodArgumentResolver 抽象类实现了 HandlerMethodArgumentResolver 接口。
首先来看下其支持解析的参数种类
再来看下其解析参数的过程
参数解析的过程可以分为三个部分:参数名字解析、参数值获取、参数值类型转换。
(1). 参数名字解析
NamedValueInfo 是该抽象解析器定义的一个内部类,有三个属性记录形参上的修饰,分别是 name、required、defaultValue,分别记录形参名字、形参是否必须、形参默认值。
咱们来看下 RequestParamMethodArgumentResolver 子类是怎么实现 createNamedValueInfo(..) 这个方法的。
很明显返回的是 RequestParamNamedValueInfo 对象,RequestParamNamedValueInfo 类是该解析类里面的一个内部类,继承自 NamedValueInfo,构建方法里面将传进去的 RequestParam 的 name、required、defaultValue 分别记录到创建的 RequestParamNamedValueInfo 对象的属性上。
通过后面的 updateNameValueInfo(..) 方法检查一遍,当 @RequestParam 注解的 name 和 value 属性为空时,会自动以形参的名字作为 name。
(2). 参数值获取
resolveName(...) 抽象类的抽象方法,具体由其子类实现,下面我们来看下 RequestParamMethodArgumentResolver 解析类是怎么实现的吧。
上面分别实现了可变请求和不可变请求对于根据 name 取值的方式。
(3). 参数值类型转换
从上面源码可以看出,通过自身绑定的 binderFactory 创建出 WebDataBinder 对象,通过创建出来的 WebDataBinder 对象来进行数据转换。
那么接下来的分析就有条理了,分为三个部分:WebDataBinderFactory 属性对象的创建及绑定、WebDataBinderFactory 属性对象内部执行 createBinder(...) 方法创建出 WebDataBinder 对象的具体逻辑、 WebDataBinder 进行数据类型转换的具体逻辑。
(3.1) WebDataBinderFactory 属性对象的创建及绑定
前面说到在 RequestMappingHandlerAdapter 适配器中执行 invokeHandlerMethod(...) 方法,通过 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); 方法创建 WebDataBinderFactory 对象,并将其绑定在创建的 ServletInvocableHandlerMethod 对象上。
这里的 this.webBindingInitializer 属性其实就是一个 ConfigurableWebBindingInitializer 对象,即在 <mvc:annotation-driven /> 时默认注册的。即包含一些解析参数需要的 MessageCodesResolver、BindingErrorProcessor、Validator、ConversionService、PropertyEditorRegistrar[]。
(3.2) WebDataBinderFactory 对象内部执行 createBinder(...) 方法创建出 WebDataBinder 对象的具体逻辑
首先来看下 WebDataBinderFactory 接口的实现类
再来看 createBinder(...) 方法
1 new 出 WebDataBinder 接口实现类,依照自身实现类为准,从 (3.1) 看出这里的 this 对象是 ServletRequestDataBinderFactory 对象,new 出来的 WebDataBinder 接口实现类应该是 ExtendedServletRequestDataBinder 类对象。
2 对 WebDataBinder 对象进行一些初始化,this.initializer 属性是在上面 (3.1) 绑定进来的 ConfigurableWebBindingInitializer 对象。
从执行方法里面可以看出设置一些我们在解析参数时用到的转换器和验证器到 WebDataBinder 对象上。
3 自身初始化 WebDataBinder 的方法
从上面 isBinderMethodApplicable(..) 匹配符合该参数转换的 @initBinder 注解修饰的方法逻辑可以看出,以后在 Controller 里面写 @initBinder 注解修饰的方法,尽量指定 value 属性字段,以免每个参数解析都执行不必要的 @initBinder 注解修饰的方法。
(3.3) WebDataBinder 进行数据类型转换的具体逻辑,执行convertIfNecessary(...) 方法
数据转换这块很复杂,我目前的能力只能做潜在的分析。因为 WebDataBinder 继承自 DataBinder,又因为 DataBinder 实现了PropertyEditorRegistry 和 TypeConverter 接口,所以该类具有注入自定义编辑器和转换数据的能力。
数据的转换最终交给 TypeConverterDelegate 类进行转换
从上面可以看出,先匹对自定义的编辑器进行数据转换,没有合适的编辑器则匹配对应的转换器进行数据转换。
再来看下第 3 步自定义编辑器里面是怎么来转换数据的
2.3 返回值析器内部解析流程
前面说到返回值处理器记录在 ServletInvocableHandlerMethod 绑定的 returnValueHandlers 属性上,returnValueHandlers 属性是一个 HandlerMethodReturnValueHandlerComposite 类,这个类是一种组合模式的变形,他也实现了 HandlerMethodReturnValueHandler 接口,并且该类里面有 returnValueHandlers 属性是 List 集合属性,缓存了所有的返回值处理器。不清楚的可以看上面的 2.1 解析器的绑定及匹配
由于返回值处理器也比较多,所以这里也选取一个最常用的 ViewNameMethodReturnValueHandler 返回值解析器看下内部实现原理。首先他肯定实现了 HandlerMethodReturnValueHandler 接口,并实现了该接口里面的两个方法。
其他相关文章
SpringMVC入门笔记
SpringMVC工作原理之处理映射[HandlerMapping]
SpringMVC工作原理之适配器[HandlerAdapter]
SpringMVC工作原理之参数解析
SpringMVC之自定义参数解析
SpringMVC工作原理之视图解析及自定义
SpingMVC之<mvc:annotation-driven/>标签