1、回顧servlet 與jsp 執(zhí)行過程

2、Spring MVC請求處理流程

3,mvc 體系結(jié)構(gòu)詳解
URL映射
表單參數(shù)映射
調(diào)用目標(biāo)Control
數(shù)據(jù)模型映射
視圖解析
異常處理

DispatcherServlet
DispatcherServlet它的作用有點(diǎn)兒類似于網(wǎng)關(guān),DispatcherServlet負(fù)責(zé)將請求轉(zhuǎn)發(fā)到不同的Controller上去。
配置DispatcherServlet

/ 后面不能寫成/*否則容易出問題。
編寫Controller,繼承controller類。實(shí)現(xiàn)handlerRequest接口。指定ModelAndView。這是一種方式。

第二種方式 繼承 HttpRequestHandler類。那么DispatcherServlet是如何去匹配不同的controller的呢?這就需要了解SpringMVC的體系結(jié)構(gòu)。整個(gè)體系結(jié)構(gòu)都搞懂才行。下面就從一個(gè)全局的視角去看Spring MVC的體系結(jié)構(gòu)。

SpringMVC的體系結(jié)構(gòu)圖

DispatcherServlet是通過HandlerMapping的一組映射關(guān)系去找到我們的目標(biāo)Controller
通過上面我們知道Controller存在的形式是多種多樣的??梢酝ㄟ^Controller接口的形式,也可以通過@Controller注解的形式存在。等等等。。。有這么多不同的Controller形式DispatcherServlet是如何去執(zhí)行的呢?這里面就涉及到一個(gè)HandlerAdapter控制器適配器。找到數(shù)據(jù)模型的一個(gè)映射。然后試圖解析是通過ViewResolver這個(gè)東東。他支持各種各樣的試圖解析。view用于具體的試圖解析。HandlerExceptionResolver異常攔截解析器。

下面就圍繞這張圖去逐一進(jìn)行源碼分析。
一,HandlerMapping源碼分析
HandlerMapping是一個(gè)接口,他有兩個(gè)實(shí)現(xiàn)類,MatchableHandlerMapping和AbstractHandlerMapping。HandlerMapping在getHandler里面并沒有返回handler(我們的目標(biāo)執(zhí)行器,也就是我們的controller)而是返回一個(gè)執(zhí)行鏈條。HandlerExecutionChain。


根據(jù)下圖可知HandlerExecutionChain里面有一個(gè)getHandler方法,這里面返回了Handler。但是具體的是哪個(gè)一個(gè)Handler是不確定的,因?yàn)樗荗bject類型的。所以通過動(dòng)態(tài)代理的方式是不能實(shí)現(xiàn)的。只能通過這個(gè)執(zhí)行鏈條去返回目標(biāo)Handler。后續(xù)的文章我們會(huì)對執(zhí)行鏈條做深入的講解。

然后看上面的類繼承關(guān)系圖。左面繼承了AbstractUrlHandlerMapping其中AbstractDetectingUrlHandlerMapping起到自動(dòng)發(fā)現(xiàn)的作用,他是根據(jù)Bean的名稱,而且是必須以/開頭。比如這樣
<bean name="/hello.do" class="com.tuling.control.SimpleControl"/>
然后SimpleUrlHandlerMapping。如果不需要自動(dòng)發(fā)現(xiàn)功能,則使用這個(gè)。SimpleUrlHandlerMapping是自己配置URL和controller之間的一個(gè)關(guān)系。所以他們之間的區(qū)別就是一個(gè)自動(dòng)發(fā)現(xiàn),一個(gè)手動(dòng)配置。AbstractHandlerMethodMapping是對方法進(jìn)行映射RequestMappingInfoHandlerMapping這個(gè)大家比較熟悉了,就是通過@RequestMapping進(jìn)行映射的。如果配置了SimpleUrlHandlerMapping或者BeanNameUrlHandlerMapping那么默認(rèn)的就會(huì)失效。SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping可以同時(shí)配置,都可以映射到我們的Controller控制器。
SimpleUrlHandlerMapping的初始化執(zhí)行流程
對initApplicationContext()方法進(jìn)行調(diào)用。其中super.initApplicationContext();這里是初始化一些攔截器。比如自動(dòng)發(fā)現(xiàn)攔截器啊等等等。this.registerHandlers(this.urlMap);開始進(jìn)行Url的匹配點(diǎn)進(jìn)去看一下可知如果urlMap不為空才進(jìn)行匹配。第一步是!url.startsWith("/")如果不是/開頭的。就會(huì)把你的URL加上/然后獲取Handler,就是我們的控制器。然后判斷handler是不是String類型的,。如果是進(jìn)行去除空格的處理。然后執(zhí)行this.registerHandler(url, handler);點(diǎn)進(jìn)去發(fā)現(xiàn),將handler轉(zhuǎn)化為Object類型。然后判斷是否是懶加載并且是String類型。然后將Handler轉(zhuǎn)化成String的字符串。然后獲取一個(gè)ApplicationContext對象。判斷handler是都是單例。如果是通過ApplicationContext.getBean(handlerName)獲取到一個(gè)Object對象。然后獲取handlerMap,判斷是否是空,如果不為空說明你配置了兩個(gè)name屬性此時(shí)會(huì)拋出異常。接著判斷你的url是否等于/如果是設(shè)置一個(gè)根目錄的Handler通過localhost:8080/即可訪問我們的handler。然后判斷是否等于/*如果是就設(shè)置一個(gè)默認(rèn)的和Handler。也就是說你永遠(yuǎn)都不會(huì)擔(dān)心找不到這個(gè)url的異常。

最后如果以上情況都不是,則通過this.handlerMap.put(urlPath, resolvedHandler);將url和handler綁定在一起。如果說handler是懶加載那么此時(shí)map中存儲(chǔ)的value就是beanName。這就是SimpleUrlHandlerMapping的初始化流程。
SimpleUrlHandlerMapping的訪問執(zhí)行流程
我們知道最終執(zhí)行調(diào)用的一定是DispatcherServlet。所以我們從這里開始入手。找到getHandler

首先通過Iterator var2 =this.handlerMappings.iterator();或取到所有的Mapping然后遍歷。遍歷一次取出一個(gè)handlerMapping然后調(diào)用handlerMapping。點(diǎn)進(jìn)去看看。第一行g(shù)etHandlerInternal這個(gè)方法很重要。點(diǎn)進(jìn)去看看,在這個(gè)方法里第一行通過getUrlPathHelper().getLookupPathForRequest(request);去解析我們地址欄上的URL。比如或取到的是/hello.do。第二行通過Object handler = lookupHandler(lookupPath, request);去查找對應(yīng)的handler。點(diǎn)進(jìn)去發(fā)現(xiàn)就是從上面put進(jìn)去的handlerMap找到對應(yīng)的handler。在最后return一個(gè)buildPathExposingHandler。點(diǎn)進(jìn)去發(fā)現(xiàn)HandlerExecutionChain這里面配置了一個(gè)攔截器鏈,那么返回的并不是我們最終的handler。

那么getHandlerInternal方法的第二行也執(zhí)行完了。最終拿到得是一個(gè)攔截器鏈,如果說攔截器鏈為空則判斷第一行的url解析器拿到得/hello.do是否等于/如果是Object rawHandler=getRootHandler()設(shè)置為根節(jié)點(diǎn)的handler,然后判斷rawHandler是否等于空,如果是則調(diào)用默認(rèn)的handler。如果不等于空呢,則直接通過beanName從Spring容器找到Handler。 再調(diào)用buildPathExposingHandler。還是獲取一個(gè)攔截器鏈。最終,將攔截器鏈返回。getHandlerInternal方法執(zhí)行結(jié)束。返回到getHandler方法里。那么如果說,沒有獲取到攔截器鏈,則獲取默認(rèn)的handler。如果不為空,則直接通過beanName從容器中獲取到handler。然后,去創(chuàng)建一個(gè)攔截器鏈條,又加入了一個(gè)攔截器。最后進(jìn)一步獲取handler。然后返回HandlerExecutionChain。最后通過HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());獲取到的才是我們的handler。SimpleUrlHandlerMapping執(zhí)行流程到此介紹完畢,說實(shí)話SpringMVC的代碼寫的跟IOC\aop差著一大塊兒的水平感覺。


Controller 接口:
HttpRequestHandler 接口:
HttpServlet 接口:
@RequestMapping方法注解
可以看出 Handler 沒有統(tǒng)一的接口,當(dāng)dispatchServlet獲取當(dāng)對應(yīng)的Handler之后如何調(diào)用呢?調(diào)用其哪個(gè)方法?這里有兩種解決辦法,一是用instanceof 判斷Handler 類型然后調(diào)用相關(guān)方法 。二是通過引入適配器實(shí)現(xiàn),每個(gè)適配器實(shí)現(xiàn)對指定Handler的調(diào)用。spring 采用后者。

HandlerAdapter詳解
HandlerAdapter中有三個(gè)接口
1supports接口,主要的作用是傳入一個(gè)handler看看是否可以被執(zhí)行。如果可以返回true。
2handle處理接口,處理完返回一個(gè)ModelAndView。
3getLastModified,用作緩存處理,獲取最后一次修改時(shí)間。

我們知道HandlerAdapter對應(yīng)適配了4種handler關(guān)系圖如下。

舉個(gè)例子,如果說我們寫了一個(gè)Servlet,然后繼承HttpServlet此時(shí)如果不配置SimpleServletHandlerAdapter適配器,那么就會(huì)報(bào)錯(cuò),報(bào)500的異常。但是如果配置了這個(gè)適配器,那么SpringMVC給我們配置的默認(rèn)適配器SimpleControllerHandlerAdapter就會(huì)失效。所以需要手動(dòng)的配置一下。下面看一下源碼即可驗(yàn)證這個(gè)說法。上面講到返回一個(gè)攔截器鏈條。然后下面的代碼就是獲取適配器如下圖。

點(diǎn)進(jìn)getHandlerAdapter方法。這個(gè)方法里做了一個(gè)do-while循環(huán)。通過support方法判斷是否是可用的適配器。

在下圖的方法里判斷是否是我們配置的適配器,如果是則返回true。 拿到適配器以后就開始了真正的調(diào)用。

此時(shí)適配器找到了以后就開始真正的調(diào)用handler也就是我們servlet或者是controller。然后通過一個(gè)叫ViewResolver的接口類去解析。其中有一個(gè)接口叫resolveViewName。他返回一個(gè)View。然后View里面有一個(gè)render接口。通過里面的model進(jìn)行處理,封裝HTML。通過response進(jìn)行寫入。所以它是先有ViewResolver才有的View。

然后看看View有哪些實(shí)現(xiàn)類如下圖。

第一個(gè)AbstractTemplateViewResolver里面有以下兩種視圖支持,比如我們常用的FreeMarkerViewReslover。
第二個(gè)BeanNameViewResolver可以通過這種方式實(shí)現(xiàn)自定的視圖解析器,生明一個(gè)自定義View類。然后實(shí)現(xiàn)View接口。在render里面去做返回。然后controller里面通過ModelAndView視圖解析器的構(gòu)造器去加載我們的自定義View類型。
第三個(gè)就是InternalResourceViewResolver。這個(gè)就是我們最常用的,通過配置前綴,后綴等信息去實(shí)現(xiàn)視圖解析。
下圖是通過BeanName的形式做的視圖解析

最后附上一張整體的流程圖
