spring-framework 版本:v5.3.19
一、請求流程
總體流程在 DispatchServelt#doDispatch 方法
1、處理器映射器
首先會獲取根據(jù)url去映射對應(yīng)的處理器(即接口執(zhí)行方法)
看到對應(yīng)的 getHandler 方法
為方便閱讀,進(jìn)入debug。可以看到springmvc默認(rèn)為我們注冊了三個handlerMapping。
springMvc中的各個組件如處理器映射器,處理器適配器,視圖解析器等是如何注入到容器的,先暫時不看,第二部分在看(問題1)。
1.1、 RequestMappingHandlerMapping
而其中我們最常用的一個就是RequestMappingHandlerMapping,所以這里只看這個類的 getHandler 方法(其實(shí)也是其抽象父類的方法)。
1.2、獲取對應(yīng)的映射方法
從 getHandlerInternal 方法入手,看看是如何把一個請求映射到對應(yīng)的方法的。
這里首先會從 mappingRegister 中去找可以根據(jù)url直接得到的映射(即沒有替換符),如果找不到再去所有注冊的映射中找,然后返回一個最匹配的映射對應(yīng)的處理器。
mappingRegister 可以簡單的理解成 Map<url,handler>。實(shí)際上,為提供效率一共維護(hù)了3個map
這些map是如何初始化的,第二部分再講(問題2)。
至于是如何匹配這些映射的,從 addMatchingMappings 入手
這里其實(shí)就是把匹配邏輯抽象成一個個condition,如果滿足所有 condition 說明這是一個可用的處理器。注意,僅是可用而已,并不一定會用這個處理器。因?yàn)榭赡芡瑫r會匹配到多個可用的處理器,但是最終只會返回最匹配的那一個。
1.3、添加攔截器
在前面的總體流程中注釋到,獲取處理器的時候,返回的是一個處理器鏈。因?yàn)榭赡軙渲糜袛r截器,而攔截器的添加從 getHandlerExecutionChain 入手。
可以看到,就只是遍歷當(dāng)前 handlerMapping 所添加的所有攔截器,若匹配,則將該攔截器添加到處理器鏈中而已。至于 handlerMapping 的攔截器是如何添加的,第二部分再講(問題3)。
2、獲取合適的處理器適配器
Spring MVC可能會有多種類型的處理器,例如控制器(Controller)、RESTful控制器等。處理器適配器負(fù)責(zé)選擇并調(diào)用合適的處理器來處理請求。這一步就是去獲取一個合適的處理器適配器,其實(shí)就是一個簡單遍歷獲取,在spring mvc 默認(rèn)提供的幾種適配器中,最常用的還是 RequestMappingHandlerAdapter
3、通過處理器適配器執(zhí)行處理器方法
3.1、攔截器的前置后置
在正式看處理器之前,順便提一嘴攔截器的執(zhí)行。
在執(zhí)行實(shí)際業(yè)務(wù)方法之前會先執(zhí)行處理器鏈中攔截器前置方法。同理,執(zhí)行完業(yè)務(wù)方法后會執(zhí)行處理器鏈中攔截器的后置方法。
攔截器的執(zhí)行就是一個遍歷,就不上代碼了。
3.2、處理器的執(zhí)行
從 ha.handle 入手
不管是哪種適配器,最終都會依次通過 參數(shù)解析器、處理器、結(jié)果處理器 將方法返回值封裝成 ModelAndView 對象。
3.2.1 參數(shù)解析器解析參數(shù)和執(zhí)行處理器方法
從 invokeForRequest 入手
處理器方法的執(zhí)行就是反射實(shí)現(xiàn)的,沒啥好看的。主要看參數(shù)解析器是如何解析參數(shù)的。
InvocableHandlerMethod 中維護(hù)了名為 resolvers 的 HandlerMethodArgumentResolverComposite 對象(多個參數(shù)解析器的封裝),并且這個變量的值是處理器適配器傳過來的。至于處理器適配器的參數(shù)解析器是哪來的,先暫時不看,后面第二部分再看(問題4)。這里只需知道參數(shù)解析器是在這里解析的以及值是處理器適配器傳過來的就可以了。
3.2.2 結(jié)果處理器處理結(jié)果
從 returnValueHandlers.handleReturnValue 入手
**跟參數(shù)解析器維護(hù)的復(fù)合對象一樣,也維護(hù)了一個名為 returnValueHandlers 的 HandlerMethodReturnValueHandlerComposite 復(fù)合對象,并且這個變量的值也是處理器適配器傳過來的。**至于處理器適配器的結(jié)果處理器是哪來的,也先暫時不看。
一般來說參數(shù)解析器的命名為 xxxMethodArgumentResolver 而結(jié)果處理器的命名為 xxxMethodReturnValueHandler。但是如果即是參數(shù)解析器又是結(jié)果處理器的命名就會xxxMethodProcessor。而我們常用的 RequestResponseBodyMethodProcessor 就是這種類型。
3.2.3 消息轉(zhuǎn)化器
首先并不是所有的參數(shù)解析器或者結(jié)果處理器都會用到消息轉(zhuǎn)化器。,但至少我們最常用的 **RequestResponseBodyMethodProcessor 無論是解析參數(shù)還是處理結(jié)果,都會用到消息轉(zhuǎn)化器。**對應(yīng)的方法分別是 readWithMessageConverters,writeWithMessageConverters。并且write是直接寫到 http 的response,這也意味著 @ResponseBody 不需要通過視圖解析器解析渲染視圖。
在 RequestResponseBodyMethodProcessor 中無論是read還是write,都會去遍歷參數(shù)解析器/結(jié)果處理器中維護(hù)的消息轉(zhuǎn)化器列表。至于 參數(shù)解析器/結(jié)果處理器的消息轉(zhuǎn)化器列表是哪里來的,也先暫時不看,后面在看(問題5)。
4、視圖解析器
試想這么一個接口,沒有 @ResponseBody 并且返回值是一個字符串。
如果有 @ResponseBody 注解,ModelAndView為null,無需解析渲染視圖。
在我們配置如下的視圖解析器時會訪問到servlet容器下的mv.html頁面。
從字符串到返回的具體頁面,這正是視圖解析器所做的事情。
關(guān)于視圖解析器,從 processDispatchResult 入手
解析:如果 ModelAndView 有視圖名(在上面的例子中視圖名就是方法返回的字符串)就會調(diào)用視圖解析器去解析視圖名得到一個視圖 View 對象,反之就從 ModelAndView 中直接獲取 View 對象??傊还茉鯓?,這里一定要有 View 對象,沒有就報錯。
渲染:然后再調(diào)用上一步得到的 View 對象 render 方法并將 ModelAndView 里的 Model 數(shù)據(jù)傳入,從而將數(shù)據(jù)渲染帶視圖上(如:jsp)。最后將渲染后的結(jié)果返回至前端。
至此一個完整的請求流程就看完了。為更形象記憶 spring mvc 各組件之間的關(guān)系,這里附上一張網(wǎng)圖
二、請求流程中的一些問題
1、各種組件如處理器映射器,處理器適配器,視圖解析器等是如何注入到spring容器的?
答:@EnableWebMvc注解(相當(dāng)于xml配置: <mvc:annotation-driven/>)。
@EnableWebMvc 引入了 DelegatingWebMvcConfiguration,而這個類中就默認(rèn)注入了 spring mvc 的各種組件。
比如:
2、RequestMappingInfoHandlerMapping 處理器映射器中維護(hù)的映射map (即MapingRegister) 是如何初始化的?
答:在 RequestMappingInfoHandlerMapping bean生命周期的 afterPropertiesSet。
3、處理器映射器中的攔截器是如何添加的?
答:以 RequestMappingInfoHandlerMapping 為例,在 DelegatingWebMvcConfiguration 注入處理器映射器的時就同時會去set攔截器。
其中 getInterceptors 代碼如下
說白了就是 getInterceptors 方法,會依次調(diào)用所有的 WebMvcConfigurer.addInteceptors 方法將自定義配置的攔截器添加到處理器映射器中的 InterceptorRegistry。之后處理器映射器在映射處理器時,將其中匹配的攔截器添加到處理鏈,進(jìn)而實(shí)現(xiàn)攔截器的效果。
正是因?yàn)樽詣幼⑷氲脑?,所以我們平時配置spring mvc時只需注入一個實(shí)現(xiàn) WebMvcConfigurer 接口的類。
但其實(shí)截至目前為止添加的還只是一個原始版本的攔截器,真正使用的攔截器是適配后的版本。
適配的時機(jī)是在處理器映射器生命周期的 setApplicationContext 方法。(最后會調(diào)用到 initApplicationContext)
4、處理器適配器的參數(shù)解析器和結(jié)果處理器是如何來的?
答:在 RequestMappingHandlerAdapter bean生命周期的 afterPropertiesSet。
具體添加了哪些參數(shù)解析器,結(jié)果處理器就不細(xì)看了,太多了…
5、參數(shù)解析器/結(jié)果處理器中的消息轉(zhuǎn)化器是如何來的?
答:來自處理器適配器,而處理器適配器的消息轉(zhuǎn)化器來自 WebMvcConfigurer 配置或者默認(rèn)配置。
如下圖:
(參數(shù)解析器/結(jié)果處理器中的消息轉(zhuǎn)化器來自處理器適配器)
(適配器的來自 WebMvcConfigurer 配置或者默認(rèn)配置)文章來源:http://www.zghlxwxcb.cn/news/detail-618983.html
至于this.configurers.configureMessageConverters(converters);
首先configurers在前面第三個問題已經(jīng)看過了,就是自動注入的 WebMvcConfigurer 總和。所以這句總的來說就是依次調(diào)用所有的 WebMvcConfigurer.configureMessageConverters 方法將自定義配置的消息添加到處理器適配器中。文章來源地址http://www.zghlxwxcb.cn/news/detail-618983.html
到了這里,關(guān)于spring5源碼篇(12)——spring-mvc請求流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!