天天看點

SpringMVC請求處理過程

接前面的“SpringMVC啟動分析”

繼續……

作為一個Servlet,請求時從doGet和doPost開始的

DispatcherServlet的doGet和doPost是從FrameworkServlet繼承來的

<a href="https://s2.51cto.com/oss/201711/02/d5d02a4bc23561b1709ffff92415f99b.png" target="_blank"></a>

processRequest()中主要是調用doService()方法,它是在DispatcherServlet中具體實作的

<a href="https://s2.51cto.com/oss/201711/02/df8b3de84ba123c0bc41e12571fad849.png" target="_blank"></a>

doService中主要是調用doDispatch方法

<a href="https://s1.51cto.com/oss/201711/02/22e87451b49eb9b4cf1af490c0fdd350.png" target="_blank"></a>

<a href="https://s4.51cto.com/oss/201711/09/51e6a29fd4f6b71a32eb691735e1bd84.png" target="_blank"></a>

這個方法就是SpringMVC處理過程的宏觀流程,從這裡可以看出大緻流程如下:

(1)判斷是否有檔案上傳,如果有,請求轉成MultipartHttpServletRequest

(2)從HandlerMapping中查找請求對應的Handler,并傳回一個HandlerExecutionChain。這個處理器執行鍊中包含了處理器和攔截器。

(3)查找Handler對應的HandlerAdapter

(4)依次調用攔截器的preHandle方法

(5)通過HandlerAdapter的handle方法去調用處理器的目标方法,并傳回ModelAndView對象

(6)依次調用攔截器的postHandle方法

(7)渲染視圖(找到具體的視圖頁面,并填充模型資料),并依次調用攔截器的afterCompletion方法

上面這個過程有幾個疑問:

疑問1:怎麼找到HandlerMapping的?

疑問2:怎麼找到HandlerAdapter?為什麼要Adapter?

下面具體來看一下

怎麼找HandlerMapping?

<a href="https://s2.51cto.com/oss/201711/15/6ed46e08630e438c792bada38162c97e.png" target="_blank"></a>

這是預設的HandlerMapping和HandlerAdapter元件,這裡我們隻看其中一種,基于注解的實作

與之對應的是DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter

先看DefaultAnnotationHandlerMapping

<a href="https://s1.51cto.com/oss/201711/09/a0bc7c769cc22d04df65d19bcf46ad27.png" target="_blank"></a>

DefaultAnnotationHandlerMapping間接實作了ApplicationContextAware接口,是以在WebApplicationContext建立完成以後會回調setApplicationContext()方法,而在這個方法中又調用了initApplicationContext()方法,它是在子類中實作的,是以,對應DefaultAnnotationHandlerMapping而言,它最終調用的是AbstractDetectingUrlHandlerMapping中的initApplicationContext()方法。下面看一下它長什麼樣?

<a href="https://s5.51cto.com/oss/201711/09/877a00ba362f9fe400b2a16adc81b387.png" target="_blank"></a>

<a href="https://s5.51cto.com/oss/201711/09/b9bc2ae5ac89a914eaf11da5d07c5b53.png" target="_blank"></a>

上面兩步所做的工作就是找到全部的映射URL,查找的依據如下:

(1)看Bean上面有沒有@RequestMapping注解,如果有,周遊Bean中所有的方法,在方法是同樣是找@RequestMapping注解,如果找到了,則兩個@RequestMapping注解的value值拼接在一起就是一個url

(2)如果Bean上面沒有@RequestMapping注解,直接在其方法上找,同樣傳回注解value值

舉個例子:

<a href="https://s1.51cto.com/oss/201711/09/1ca79ad36df0a185a7a2ee6155f20522.png" target="_blank"></a>

這個Controller會産生3個url映射

<a href="https://s5.51cto.com/oss/201711/09/9143b43c2e6980c61485573fa9fe0732.png" target="_blank"></a>

這樣就可以找到所有的映射Url,并以數組形式傳回。也就是說,每次傳回的String[]中的url對應的處理器是同一個。

到此為止,我們就知道了,是怎麼根據請求uri找到對應的Controller的,原來是因為SpringMVC啟動的時候就已經建立好了這種映射關系。

這裡順帶提一句,預設的HandlerMapping并不隻是DefaultAnnotationHandlerMapping這一個,還有一個是BeanNameUrlHandlerMapping,它是根據Bean的名字來識别的,以"/"開頭的就是。

<a href="https://s5.51cto.com/oss/201711/15/f92e93b0db7fa11554159814cb9c3e54.png" target="_blank"></a>

接下來看第2個問題

HandlerAdapter是幹什麼的?為什麼需要Adapter?

首先,為什麼需要Adapter,明明已經拿到Handler對象了幹嘛不直接調用具體的方法呢,還要通過Adapter去調用具體的方法?

我覺得,有這樣幾個原原因

1、屏蔽底層實作的差異和複雜度。建立Controller的方式有很多種,基于注解來建立隻是其中一種,不同類型的處理器,有不同類型的Adapter,但最終還是HandlerAdapter。(PS:感覺有點像政策模式呀)

2、适配不同類型的處理器。這跟第一點很類似,Adapter擴充卡,就想一個插排一樣,可以适配各種插頭,HandlerAdapter就是插排,其實作類就是插頭。

<a href="https://s5.51cto.com/oss/201711/15/41f7d6fa45783037284b5ed21d26b846.png" target="_blank"></a>

接下來,具體看一下AnnotationMethodHandlerAdapter是如何處理的

<a href="https://s2.51cto.com/oss/201711/15/5fff7c56c65ab44240ac0e7c08fb3cb8.png" target="_blank"></a>

找Handler中有@RequestMapping注解的方法,通過url比對到具體的Method,這個Method已經是反射後的Method對象,然後調用。具體步驟其實很複雜,這裡不細看了。

到此為止,請求處理部分就算是完結了~~~

本文轉自   手不要亂摸  51CTO部落格,原文連結:http://blog.51cto.com/5880861/1981945

繼續閱讀