SpringMVC工作原理之參數(shù)解析

前面分析到 SpringMVC工作原理之處理映射[HandlerMapping] 豹芯,由映射處理器(HandlerMapping) 解析出對應(yīng)的 handler意荤。接著 SpringMVC工作原理之適配器[HandlerAdapter] 描述了 handler 是怎么匹配到合適的適配器海诲,進(jìn)行 handler 對應(yīng)方法的執(zhí)行炮叶。其他幾種適配器還好哥遮,但是 RequestMappingHandlerAdapter 適配器對應(yīng)接下來的參數(shù)解析及綁定并執(zhí)行并不是那么簡單笨使,因此本篇筆記主要分析 RequestMappingHandlerAdapter 適配器解析對應(yīng) handler 的執(zhí)行流程。
本篇筆記主要分析SpringMVC 5.1.1 這個(gè)版本竞阐。

SpringMVC運(yùn)行流程

RequestMappingHandlerAdapter 大概解析流程如下

RequestMappingHandlerAdapter解析流程

1 了解在前面

在開始下面的具體源碼分析前缴饭,我們需要了解一些相關(guān)的類和接口

1.1 HandlerMethod

在開始記錄方法執(zhí)行流程前,必須要先說下記錄方法的對象 HandlerMethod骆莹,HandlerMethod 及子類主要用于封裝方法調(diào)用相關(guān)信息颗搂。簡單理解為保持方法信息的 pojo 類。

HandlerMethod及其子類.png

分析下各個(gè)類的功能及職責(zé):

  • HandlerMethod 封裝方法定義相關(guān)的信息 (如類幕垦、方法峭火、參數(shù)等)
  • InvocableHandlerMethod 參數(shù)準(zhǔn)備委托 HandlerMethodArgumentResolver 進(jìn)行具體的解析
  • ServletInvocableHandlerMethod 添加返回值處理職責(zé),ResponseStatus 處理

在容器初始化的時(shí)候智嚷,RequestMappingHandlerMapping 映射處理器就將 @RequestMapping 描述的方法以 RequestMappingInfo 為 key卖丸,HandlerMethod 為 value 放進(jìn)自己的緩存 。至如 HandlerMethod 內(nèi)部后面是怎么進(jìn)行對應(yīng)方法上的參數(shù)解析及綁定到后來的方法執(zhí)行等等盏道,咱們接下來會詳細(xì)講解稍浆。

1.2 參數(shù)解析器(HandlerMethodArgumentResolver)和返回值的解析器(HandlerMethodReturnValueHandler)

在分析源碼之前,首先讓我們來看下SpringMVC中兩個(gè)重要的接口猜嘱,兩個(gè)接口都是在 3.1 版本后添加的衅枫。

  • 處理方法參數(shù)的解析器接口
  • 處理方法調(diào)用返回值的解析器接口

兩個(gè)接口分別有兩個(gè)方法,一個(gè)用來查看該解析器是否支持該參數(shù)的解析朗伶,第二個(gè)方法用來對參數(shù)進(jìn)行解析弦撩。

1.3 默認(rèn)解析器的注入

在容器初始話的時(shí)候,初始化 RequestMappingHandlerAdapter 適配器的時(shí)候會將默認(rèn)的參數(shù)解析器都注入進(jìn)緩存中论皆。

加載默認(rèn)的參數(shù)解析器(ArgumentResolvers)益楼,綁定到 RequestMappingHandlerAdapter 適配器的 argumentResolvers 屬性上。
加載默認(rèn)的返回值解析器(ReturnValueHandlers)点晴,綁定到 RequestMappingHandlerAdapter 適配器的 returnValueHandlers 屬性上感凤。

下面我們來簡單的看下都有哪些默認(rèn)解析器

  • 默認(rèn)注入的參數(shù)解析器
  • 默認(rèn)注入的返回值解析器

2 解析過程流程

2.1 解析器的綁定及匹配

接著 RequestMappingHandlerAdapter 適配器的 handleInternal(..) 方法往下說,在 handleInternal(..) 方法中主要檢查是否需要同步執(zhí)行接下來對方法的操作粒督,內(nèi)部調(diào)用 invokeHandlerMethod(..) 方法陪竿。

該方法內(nèi)部就方法執(zhí)行流程大致可以分為以上標(biāo)注的 6 步:

①. 對應(yīng) WebDataBinderFactory 工廠類的創(chuàng)建,因里面涉及到的東西有點(diǎn)多屠橄,將放在下面參數(shù)值類型轉(zhuǎn)換部分詳細(xì)解說族跛。
②. 根據(jù)該 HandlerMethod 創(chuàng)建對應(yīng)的 ServletInvocableHandlerMethod 對象。
③. 將注入到緩存的參數(shù)解析器綁定到創(chuàng)建的 ServletInvocableHandlerMethod 對象上锐墙。
④. 將注入緩存的返回值解析器綁定到創(chuàng)建的 ServletInvocableHandlerMethod 對象上礁哄。
⑤. 將上面創(chuàng)建的 WebDataBinderFactory 工廠類對象綁定到創(chuàng)建的 ServletInvocableHandlerMethod 對象上。
⑥. 執(zhí)行 ServletInvocableHandlerMethodinvokeAndHandle(...) 方法贮匕。

總結(jié):RequestMappingHandlerAdapter 在內(nèi)部對于每個(gè)請求姐仅,都會實(shí)例化一個(gè) ServletInvocableHandlerMethod 進(jìn)行處理。

ServletInvocableHandlerMethod 內(nèi)部會分別對請求跟響應(yīng)進(jìn)行處理。

進(jìn)入執(zhí)行請求對應(yīng)方法里面看看流程

接下來就是請求參數(shù)解析器和返回值解析器上場的時(shí)候了掏膏。

①. ServletInvocableHandlerMethod 類在處理參數(shù)的時(shí)候劳翰,會使用自己綁定的參數(shù)解析器,參數(shù)解析器記錄在屬性argumentResolvers (這個(gè)屬性是它的父類 InvocableHandlerMethod中定義的)馒疹,argumentResolvers 屬性是一個(gè) HandlerMethodArgumentResolverComposite 類(這里使用了組合模式的一種變形)佳簸,這個(gè)類是實(shí)現(xiàn)了 HandlerMethodArgumentResolver 接口的類,實(shí)現(xiàn)了該類里面的兩個(gè)接口颖变。同時(shí)里面有記錄所有參數(shù)解析器的 List 集合生均,有緩存 MethodParameter 與解析器對應(yīng)關(guān)系的 Map 集合。

②. ServletInvocableHandlerMethod 類在處理返回值的時(shí)候腥刹,會使用自身綁定的返回值解析器马胧,該解析器記錄在屬性 returnValueHandlers (自身屬性),returnValueHandlers 屬性是一個(gè) HandlerMethodReturnValueHandlerComposite 類(這里使用了組合模式的一種變形)衔峰,這個(gè)類實(shí)現(xiàn)了 HandlerMethodReturnValueHandler 接口佩脊,實(shí)現(xiàn)了該接口里面的兩個(gè)方法。同時(shí)里面有記錄所有返回值解析器的 List 集合垫卤。

2.2 參數(shù)解析器內(nèi)部解析流程

因?yàn)榻馕銎魈嗤茫@里只能抽其中一個(gè)來了解下參數(shù)解析器內(nèi)部實(shí)現(xiàn)解析的邏輯,選個(gè)最常用的解析器 RequestParamMethodArgumentResolver穴肘,他是用來解析 @RequestParam 注解的參數(shù)歇盼。RequestParamMethodArgumentResolver 繼承自 AbstractNamedValueMethodArgumentResolver,而 AbstractNamedValueMethodArgumentResolver 抽象類實(shí)現(xiàn)了 HandlerMethodArgumentResolver 接口评抚。

首先來看下其支持解析的參數(shù)種類

再來看下其解析參數(shù)的過程

參數(shù)解析的過程可以分為三個(gè)部分:參數(shù)名字解析豹缀、參數(shù)值獲取、參數(shù)值類型轉(zhuǎn)換盈咳。

(1). 參數(shù)名字解析

NamedValueInfo 是該抽象解析器定義的一個(gè)內(nèi)部類耿眉,有三個(gè)屬性記錄形參上的修飾,分別是 name鱼响、requireddefaultValue组底,分別記錄形參名字丈积、形參是否必須、形參默認(rèn)值债鸡。

咱們來看下 RequestParamMethodArgumentResolver 子類是怎么實(shí)現(xiàn) createNamedValueInfo(..) 這個(gè)方法的江滨。

很明顯返回的是 RequestParamNamedValueInfo 對象,RequestParamNamedValueInfo 類是該解析類里面的一個(gè)內(nèi)部類厌均,繼承自 NamedValueInfo唬滑,構(gòu)建方法里面將傳進(jìn)去的 RequestParamnamerequireddefaultValue 分別記錄到創(chuàng)建的 RequestParamNamedValueInfo 對象的屬性上晶密。

通過后面的 updateNameValueInfo(..) 方法檢查一遍擒悬,當(dāng) @RequestParam 注解的 namevalue 屬性為空時(shí),會自動(dòng)以形參的名字作為 name稻艰。

(2). 參數(shù)值獲取

resolveName(...) 抽象類的抽象方法懂牧,具體由其子類實(shí)現(xiàn),下面我們來看下 RequestParamMethodArgumentResolver 解析類是怎么實(shí)現(xiàn)的吧尊勿。

上面分別實(shí)現(xiàn)了可變請求和不可變請求對于根據(jù) name 取值的方式僧凤。

(3). 參數(shù)值類型轉(zhuǎn)換

從上面源碼可以看出,通過自身綁定的 binderFactory 創(chuàng)建出 WebDataBinder 對象元扔,通過創(chuàng)建出來的 WebDataBinder 對象來進(jìn)行數(shù)據(jù)轉(zhuǎn)換躯保。
那么接下來的分析就有條理了,分為三個(gè)部分:WebDataBinderFactory 屬性對象的創(chuàng)建及綁定澎语、WebDataBinderFactory 屬性對象內(nèi)部執(zhí)行 createBinder(...) 方法創(chuàng)建出 WebDataBinder 對象的具體邏輯吻氧、 WebDataBinder 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換的具體邏輯。

(3.1) WebDataBinderFactory 屬性對象的創(chuàng)建及綁定

前面說到在 RequestMappingHandlerAdapter 適配器中執(zhí)行 invokeHandlerMethod(...) 方法咏连,通過 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); 方法創(chuàng)建 WebDataBinderFactory 對象盯孙,并將其綁定在創(chuàng)建的 ServletInvocableHandlerMethod 對象上。

這里的 this.webBindingInitializer 屬性其實(shí)就是一個(gè) ConfigurableWebBindingInitializer 對象祟滴,即在 <mvc:annotation-driven /> 時(shí)默認(rèn)注冊的振惰。即包含一些解析參數(shù)需要的 MessageCodesResolverBindingErrorProcessor垄懂、Validator骑晶、ConversionServicePropertyEditorRegistrar[]草慧。

(3.2) WebDataBinderFactory 對象內(nèi)部執(zhí)行 createBinder(...) 方法創(chuàng)建出 WebDataBinder 對象的具體邏輯

首先來看下 WebDataBinderFactory 接口的實(shí)現(xiàn)類

再來看 createBinder(...) 方法

1 newWebDataBinder 接口實(shí)現(xiàn)類桶蛔,依照自身實(shí)現(xiàn)類為準(zhǔn),從 (3.1) 看出這里的 this 對象是 ServletRequestDataBinderFactory 對象漫谷,new 出來的 WebDataBinder 接口實(shí)現(xiàn)類應(yīng)該是 ExtendedServletRequestDataBinder 類對象仔雷。

2 對 WebDataBinder 對象進(jìn)行一些初始化,this.initializer 屬性是在上面 (3.1) 綁定進(jìn)來的 ConfigurableWebBindingInitializer 對象舔示。

從執(zhí)行方法里面可以看出設(shè)置一些我們在解析參數(shù)時(shí)用到的轉(zhuǎn)換器和驗(yàn)證器到 WebDataBinder 對象上碟婆。

3 自身初始化 WebDataBinder 的方法

從上面 isBinderMethodApplicable(..) 匹配符合該參數(shù)轉(zhuǎn)換的 @initBinder 注解修飾的方法邏輯可以看出,以后在 Controller 里面寫 @initBinder 注解修飾的方法惕稻,盡量指定 value 屬性字段竖共,以免每個(gè)參數(shù)解析都執(zhí)行不必要的 @initBinder 注解修飾的方法。

(3.3) WebDataBinder 進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換的具體邏輯俺祠,執(zhí)行convertIfNecessary(...) 方法

數(shù)據(jù)轉(zhuǎn)換這塊很復(fù)雜公给,我目前的能力只能做潛在的分析借帘。因?yàn)?WebDataBinder 繼承自 DataBinder,又因?yàn)?DataBinder 實(shí)現(xiàn)了PropertyEditorRegistryTypeConverter 接口淌铐,所以該類具有注入自定義編輯器和轉(zhuǎn)換數(shù)據(jù)的能力肺然。
數(shù)據(jù)的轉(zhuǎn)換最終交給 TypeConverterDelegate 類進(jìn)行轉(zhuǎn)換

從上面可以看出,先匹對自定義的編輯器進(jìn)行數(shù)據(jù)轉(zhuǎn)換匣沼,沒有合適的編輯器則匹配對應(yīng)的轉(zhuǎn)換器進(jìn)行數(shù)據(jù)轉(zhuǎn)換狰挡。
再來看下第 3 步自定義編輯器里面是怎么來轉(zhuǎn)換數(shù)據(jù)的

2.3 返回值析器內(nèi)部解析流程

前面說到返回值處理器記錄在 ServletInvocableHandlerMethod 綁定的 returnValueHandlers 屬性上,returnValueHandlers 屬性是一個(gè) HandlerMethodReturnValueHandlerComposite 類释涛,這個(gè)類是一種組合模式的變形加叁,他也實(shí)現(xiàn)了 HandlerMethodReturnValueHandler 接口,并且該類里面有 returnValueHandlers 屬性是 List 集合屬性唇撬,緩存了所有的返回值處理器它匕。不清楚的可以看上面的 2.1 解析器的綁定及匹配

由于返回值處理器也比較多,所以這里也選取一個(gè)最常用的 ViewNameMethodReturnValueHandler 返回值解析器看下內(nèi)部實(shí)現(xiàn)原理窖认。首先他肯定實(shí)現(xiàn)了 HandlerMethodReturnValueHandler 接口豫柬,并實(shí)現(xiàn)了該接口里面的兩個(gè)方法。

其他相關(guān)文章

SpringMVC入門筆記
SpringMVC工作原理之處理映射[HandlerMapping]
SpringMVC工作原理之適配器[HandlerAdapter]
SpringMVC工作原理之參數(shù)解析
SpringMVC之自定義參數(shù)解析
SpringMVC工作原理之視圖解析及自定義
SpingMVC之<mvc:annotation-driven/>標(biāo)簽

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扑浸,一起剝皮案震驚了整個(gè)濱河市烧给,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喝噪,老刑警劉巖础嫡,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酝惧,居然都是意外死亡榴鼎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門晚唇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巫财,“玉大人,你說我怎么就攤上這事哩陕∑较睿” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵萌踱,是天一觀的道長葵礼。 經(jīng)常有香客問我,道長并鸵,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任扔涧,我火速辦了婚禮园担,結(jié)果婚禮上届谈,老公的妹妹穿的比我還像新娘。我一直安慰自己弯汰,他們只是感情好艰山,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著咏闪,像睡著了一般曙搬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鸽嫂,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天纵装,我揣著相機(jī)與錄音,去河邊找鬼据某。 笑死橡娄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的癣籽。 我是一名探鬼主播挽唉,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筷狼!你這毒婦竟也來了瓶籽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤埂材,失蹤者是張志新(化名)和其女友劉穎塑顺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楞遏,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茬暇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寡喝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糙俗。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖预鬓,靈堂內(nèi)的尸體忽然破棺而出巧骚,到底是詐尸還是另有隱情,我是刑警寧澤格二,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布劈彪,位于F島的核電站,受9級特大地震影響顶猜,放射性物質(zhì)發(fā)生泄漏沧奴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一长窄、第九天 我趴在偏房一處隱蔽的房頂上張望滔吠。 院中可真熱鬧纲菌,春花似錦、人聲如沸疮绷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冬骚。三九已至椅贱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間只冻,已是汗流浹背庇麦。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留属愤,地道東北人女器。 一個(gè)月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像住诸,于是被迫代替她去往敵國和親驾胆。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容