HandlerMapping的類繼承圖如下:
其中有一個(gè)DefaultAnnotationHanlerMapping過時(shí)了,就沒有畫出來(lái),頂級(jí)接口HandlerMapping,只定義了一個(gè)方法getHandler,而這個(gè)方法被AbstractHandlerMapping實(shí)現(xiàn)了,而在AbstractHandlerMapping的getHandler方法里定義了一個(gè)getHandlerInternal這個(gè)方法,這個(gè)方法交由繼承AbstractHandlerMapping的子孫類去實(shí)現(xiàn),也就是用了常見的設(shè)計(jì)模式,模板方法模式,父類定義整個(gè)結(jié)構(gòu)或者模板,由子類去實(shí)現(xiàn),很多第三方工具類中這種設(shè)計(jì)模式很常見.
一.初始化
HandlerMapping的組件是什么時(shí)候初始化的,這個(gè)就要說到SpringMvc的核心類DispatcherServlet,DispatcherServlet實(shí)現(xiàn)了Servelt標(biāo)準(zhǔn),會(huì)被當(dāng)做一個(gè)一個(gè)執(zhí)行鏈的一個(gè)對(duì)象添加到servlet的鏈中,這個(gè)是用了責(zé)任鏈模式,就像糖葫蘆串一樣,肯定是吃完第一個(gè)吃第二個(gè),當(dāng)項(xiàng)目初始化的時(shí)候,依次調(diào)用init方法,總會(huì)有調(diào)用DispatcherServletinit方法的一天(大致是這樣,實(shí)際是由父類FramWorkServlet根據(jù)不同的情況調(diào)用了onfresh方法,onfresh調(diào)用initStrategies方法,加載9個(gè)組件),其中initHandlerMappings就是handlerMapping組件,這幾個(gè)組件加載都有一個(gè)共性,就是先是從容器中加載對(duì)應(yīng)的組件對(duì)象,如果沒有這個(gè)對(duì)象,則拋出NoSuchBeanException異常,在這個(gè)異常里面去加載spring的默認(rèn)配置,(MultipartResolver除外,如果沒有配置是沒有默認(rèn)組件的),這些默認(rèn)的配置就在spring-webmvc\org\springframework\web\servlet包下,有一個(gè)DispatcherServlet.properties配置文件,這些配置文件中配置了默認(rèn)的一些組件,有的組件會(huì)有多個(gè)配置,在加載的時(shí)候只會(huì)加載第一個(gè).
接著說HandlerMapping的加載,hanlerMapping也是采用此種手法,但是如果沒有配置,會(huì)從容器中獲取,通常會(huì)是多個(gè),這些對(duì)象是保存在DispatcherServlet的handlerMappings這個(gè)集合當(dāng)中.將來(lái)有請(qǐng)求過來(lái),會(huì)遍歷這個(gè)list,從中找到能夠處理這個(gè)url的handler,那這就要說到這些組件是初始化url對(duì)handler的映射的.
剛剛說到實(shí)際上在dispatcherServelt容器在初始化的時(shí)候,handler組件的對(duì)象其實(shí)已經(jīng)就在spring容器了,initHandlerMappings的作用只不過是將他們放到集合當(dāng)中,那這些handler何時(shí)加載自己的配置的?這里舉該組件三個(gè)類來(lái)說
1.SimpleUrlHandlerMapping
SimpleUrlHanlerMapping里面的初始化是重寫了其父類AbstractHandlerMapping的initApplicationContext方法,這個(gè)方法是由AbstractHandlerMapping繼承WebApplicationObjectSupport這個(gè)類實(shí)現(xiàn)來(lái)的,當(dāng)spring容器啟動(dòng)時(shí)會(huì)初始化這些方法,那我們看看AbstractHandlerMapping的initApplicationContext方法干了啥,貼代碼:
這三個(gè)方法都和initInterceptor有關(guān),第一個(gè)方法extendInterceptors是個(gè)模板方法,目前在子類中并沒有實(shí)現(xiàn),而第二個(gè)字面意思是查找映射過的攔截器主要作用是用來(lái)查找已經(jīng)配置自定義的攔截器和spring自己的攔截器,似乎關(guān)系不大,而下面這個(gè)方法是初始化攔截器,似乎也和handler沒關(guān)系,那我們現(xiàn)在知道了,父類AbstractHandlerMapping作為頂級(jí)定義,他的初始化方法只加載了攔截器,其實(shí)換個(gè)角度想想也是,攔截器是通用的,而不同的HandlerMapping實(shí)現(xiàn)的策略肯定不一樣,不然抽象出AbstractHandlerMapping就沒有意義,那我們接著看看SimpleUrlHanlerMapping重寫了initApplicationContext方法是做了什么事情.
首先,他上來(lái)二話不說先調(diào)用了父類的方法,加載了攔截器的信息,接下來(lái)這個(gè)registerHandlers似乎是重點(diǎn),register是注冊(cè)的意思,他似乎要開始注冊(cè)handler了,看下代碼
他內(nèi)部維護(hù)了一個(gè)map,當(dāng)注冊(cè)handler的時(shí)候他就去遍歷這個(gè)map,map的key是url,object就是Handler,那這個(gè)map是怎么來(lái)的,這就要說到SimpleUrlHandlerMapping的用法,如果配了用SimpleUrlHandlerMapping,那么control需要繼承AbstractController方法,里面有個(gè)handleRequestInternal方法需要實(shí)現(xiàn),再在配置文件中加上請(qǐng)求的路徑和對(duì)應(yīng)的類,這個(gè)就是map的由來(lái),
一個(gè)control只有一個(gè)方法,但是一個(gè)方法可以有多個(gè)url,然后接著看registerHandler方法,這是父類的方法,也就是抽象出來(lái)公有的方法,我們看下
這個(gè)方法很重要,他是先根據(jù)handler的名字去容器中找到這個(gè)對(duì)象,然后在從handlerMap中去根據(jù)url取這個(gè)handler,如果說url里面有這個(gè)handler,就將根據(jù)名字取的和根據(jù)url取的進(jìn)行對(duì)比,看是不是同一個(gè),如果不是同一個(gè)則拋異常,如果說根據(jù)url取的handler是空的,說明還沒有注冊(cè),他又判斷了這個(gè)url是不是"/"這個(gè)請(qǐng)求,如果是則將這個(gè)傳進(jìn)來(lái)的handler設(shè)置為根handler,如果url等于"/*",他又將這個(gè)handler設(shè)置為默認(rèn)的handler,如果都不是就將他放進(jìn)這個(gè)urlMap當(dāng)中,就是將url和handler進(jìn)行注冊(cè),將來(lái)可以通過getHandler方法,取到handler.
2.BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping內(nèi)部就一個(gè)方法,那他的初始化方法肯定不是自己實(shí)現(xiàn)了,而是繼承了父類的方法,我們就看下AbstractDetectingUrlHandlerMapping類
結(jié)構(gòu)和SimpleUrlHandlerMapping方法類似,也是先調(diào)用了父類的initApplicationContext()方法,加載攔截器,然后調(diào)用自己的detectHandlers方法
重點(diǎn)看下這個(gè)方法
他首先是獲取了整個(gè)容器中所有的bean對(duì)象,然后去遍歷這些bean,在遍歷bean的時(shí)候,他有個(gè)方法determineUrlsForHandler,這個(gè)方法正是BeanNameUrlHandlerMapping唯一的方法
BeanNameUrlHandlerMapping是控制器的name作為url,所以在這個(gè)方法里,他先判斷了這個(gè)bean的名字是不是已"/"開頭,如果不是的話又去獲取他的別名,遍歷他的別名,判斷是不是以"/"開頭,最后將最終結(jié)果返回給detectHandlers方法,最后又調(diào)用了registerHandler,也就是上面我們分析的AbstractHandlerMapping的registerHandler的方法,我們看下BeanNameUrlHandlerMapping的用法:
他在control層和SimpleUrlHandlerMapping一樣都需要繼承AbstractController,不同的是配置文件
也可以在此配置他的別名,將來(lái)別名和本名就是兩個(gè)url通過一個(gè)control處理
3.RequestMappingHandlerMapping
這個(gè)組件也就是我們常用的根據(jù)@Controller和@RequestMapping來(lái)映射,內(nèi)部比上面兩個(gè)較復(fù)雜,他是實(shí)現(xiàn)了InitializingBean接口,當(dāng)這個(gè)bean被創(chuàng)建的時(shí)候,會(huì)調(diào)用InitializingBean的afterPropertiesSet方法,這個(gè)方法也是加載RequestMappingHandlerMapping的入口
config對(duì)象主要是用于根據(jù)@Request對(duì)象里面不同的屬性,找到相對(duì)應(yīng)的handler,暫且可以放后面說,先說下super.afterPropertiesSet()方法,該方法又調(diào)用了initHandlerMethods
該方法的執(zhí)行過程和BeanNameUrlHandlerMapping有異曲同工之妙,他首先也是獲取了整個(gè)容器中的bean對(duì)象,然后去判斷是不是handler,我們看下isHandler方法
也就說他判斷從spring容器取出bean是不是handler條件就是這個(gè)對(duì)象有沒有Control和RequstMapping這兩個(gè)注解,如果他是handler,他又根據(jù)這個(gè)handler去查找他的方法,之前的hanlder都是對(duì)象級(jí)別的,因?yàn)榫鸵粋€(gè)方法,而RequestMappingHandlerMapping實(shí)現(xiàn)了handler可以是方法級(jí)別的,
g
這里主要做的就是根據(jù)這個(gè)對(duì)象找到能夠作為handler的方法然后放進(jìn)一個(gè)map當(dāng)中,其中key就是根據(jù)@RequestMapping中的value封裝成的RequestMappingInfo對(duì)象,他的key會(huì)被RequestMappingInfo存在patternsCondition 當(dāng)中,可以看下結(jié)構(gòu):
緊接著遍歷這些方法,重點(diǎn)是registerHandlerMethod這個(gè)方法
它讓mappingRegisterry調(diào)用了他的register方法.先看下mappingRegisterry是個(gè)啥玩意
他的內(nèi)部也是一個(gè)個(gè)鍵值的對(duì)象,然后我們接著看下register方法做了啥
讀寫鎖這個(gè)東西先不說,首先他是把handler和method封裝成了一個(gè)HandlerMethod對(duì)象,
然后直接將mapping和handlerMethod放入一個(gè)map當(dāng)中,此時(shí)的mapping還是剛剛說的RequestMappingInfo對(duì)象,緊接著他就去遍歷了mapping里面的patternsCondition的值,也就是@RequestMapping的url,然后在去遍歷這個(gè)url值,將url和mapping添加到urlLookup當(dāng)中,緊接著又做了一個(gè)命名策略的事,就是將類中的大寫字母提取出來(lái)后面緊跟#號(hào)再加方法的名字,
addMappingName就是把這個(gè)name為鍵,這個(gè)類和方法名放入到nameLookUp當(dāng)中去
在接著就是處理了@CrossOrigin注解,這個(gè)注解是springmvc-4.2新加的功能,.主要是解決跨域問題,和我們分析的關(guān)系不大最后就是將剛剛說的幾個(gè)map封裝成了一個(gè)類,以@RequestMapping的url為值放到了一個(gè)map當(dāng)中,至此,整個(gè)加載過程分析結(jié)束
RequestMappingHandlerMapping功能很強(qiáng)大,在設(shè)計(jì)上也會(huì)相對(duì)來(lái)說比較繁瑣,這里面省略了很多細(xì)節(jié)的東西,比如根據(jù)@Request里面的值,創(chuàng)建不同的對(duì)象以便將來(lái)用相應(yīng)的策略來(lái)查找handler等等.
二.查找handler
上面說過了springmvc是如何加載handler以及spring容器是如何初始化handler的,現(xiàn)在說一說springmvc是如何查找handler的.
查找的入口在DispatcherServlet的doDispatch方法當(dāng)中
我們看下getHandler方法
這里他實(shí)際上去遍歷了之前加載的handler組件,然后查找那一個(gè)handler能狗組裝出執(zhí)行鏈,這個(gè)方法,在父類AbstractHandlerMapping中有定義
其中g(shù)etHandlerInternal方法也是個(gè)模版方法,各個(gè)子類實(shí)現(xiàn)的方法不同,所以由他們提供而返回的handlerChain里面則封裝了具體的handler和攔截器,將來(lái)可以在執(zhí)行具體的方法之前執(zhí)行攔截器的方法,就達(dá)到了攔截的效果,最核心的還是getHandlerInternal這個(gè)方法,獲取handler我們還是以剛剛的三個(gè)類分析,但是可以分為兩種,beanNameUrlHandlerMapping和SimpleUrlHandlerMapping都是繼承AbstractUrlHandlerMapping,所以實(shí)現(xiàn)由他實(shí)現(xiàn),我們看下他的方法
1.AbstractUrlHandlerMapping
他顯示從request中取到訪問請(qǐng)求的路徑,在從之前添加的map當(dāng)中去找這個(gè)handler,緊接著又根據(jù)url判斷了一些情況,基本和添加的思路相反,不多說.
2.RequestMappingHandlerMapping
他的查找方法來(lái)自于AbstractHandlerMethodMapping方法
他的邏輯首先也是獲取到url,然后在根據(jù)url去獲取handler,其中主要是lookupHandlerMethod這個(gè)方法
他在這里主要做的事情就是根據(jù)之前添加進(jìn)去的url和handler找到合適的handler,如url添加的是"/test/{id}",他會(huì)去找到合適的請(qǐng)求,這里就不細(xì)說了,有興趣可以研究研究