SpringMVC工作原理之参数解析

前面分析到 SpringMVC工作原理之处理映射[HandlerMapping] ,由映射处理器(HandlerMapping) 解析出对应的 handler。接着 SpringMVC工作原理之适配器[HandlerAdapter] 描述了 handler 是怎么匹配到合适的适配器,进行 handler 对应方法的执行。其他几种适配器还好,但是 RequestMappingHandlerAdapter 适配器对应接下来的参数解析及绑定并执行并不是那么简单,因此本篇笔记主要分析 RequestMappingHandlerAdapter 适配器解析对应 handler 的执行流程。
本篇笔记主要分析SpringMVC 5.1.1 这个版本。

SpringMVC运行流程

RequestMappingHandlerAdapter 大概解析流程如下

RequestMappingHandlerAdapter解析流程

1 了解在前面

在开始下面的具体源码分析前,我们需要了解一些相关的类和接口

1.1 HandlerMethod

在开始记录方法执行流程前,必须要先说下记录方法的对象 HandlerMethodHandlerMethod 及子类主要用于封装方法调用相关信息。简单理解为保持方法信息的 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 对象上。
⑥. 执行 ServletInvocableHandlerMethodinvokeAndHandle(...) 方法。

总结: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 是该抽象解析器定义的一个内部类,有三个属性记录形参上的修饰,分别是 namerequireddefaultValue,分别记录形参名字、形参是否必须、形参默认值。

咱们来看下 RequestParamMethodArgumentResolver 子类是怎么实现 createNamedValueInfo(..) 这个方法的。

很明显返回的是 RequestParamNamedValueInfo 对象,RequestParamNamedValueInfo 类是该解析类里面的一个内部类,继承自 NamedValueInfo,构建方法里面将传进去的 RequestParamnamerequireddefaultValue 分别记录到创建的 RequestParamNamedValueInfo 对象的属性上。

通过后面的 updateNameValueInfo(..) 方法检查一遍,当 @RequestParam 注解的 namevalue 属性为空时,会自动以形参的名字作为 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 /> 时默认注册的。即包含一些解析参数需要的 MessageCodesResolverBindingErrorProcessorValidatorConversionServicePropertyEditorRegistrar[]

(3.2) WebDataBinderFactory 对象内部执行 createBinder(...) 方法创建出 WebDataBinder 对象的具体逻辑

首先来看下 WebDataBinderFactory 接口的实现类

再来看 createBinder(...) 方法

1 newWebDataBinder 接口实现类,依照自身实现类为准,从 (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 实现了PropertyEditorRegistryTypeConverter 接口,所以该类具有注入自定义编辑器和转换数据的能力。
数据的转换最终交给 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/>标签

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容