溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringMVC中的handlerMappings對象怎么用

發(fā)布時(shí)間:2021-09-26 15:51:03 來源:億速云 閱讀:123 作者:小新 欄目:開發(fā)技術(shù)

這篇文章給大家分享的是有關(guān)SpringMVC中的handlerMappings對象怎么用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

從SpringMVC源碼解析所用的例子,一個(gè)http://localhost:9090/web/hi?name=yang請求調(diào)用到下面的地方,發(fā)現(xiàn)一個(gè)特殊的對象handlerMappings,通過請求uri去找handler的時(shí)候需要通過這個(gè)handlerMapping來找,那么handlerMappings是怎么樣的呢?

SpringMVC中的handlerMappings對象怎么用

從斷點(diǎn)調(diào)試中,我們可以看到這handlerMappings一共有5個(gè),分別是WebMvcConfigurationSupportXXX,RequestMappingHandlerMapping和BeanNameUrlHandlerMapping。

首先,我們先了解一下這個(gè)handlerMappings是什么?從官方描述看,handlerMappings是DispatcherServlet內(nèi)部的一種Bean類型,DispatcherServlet內(nèi)部有很多Bean類型,包括HandlerMapping,HandlerAdapter,HandlerExceptionResolver,ViewResolver,LocaleResolver ,ThemesResolver,Multipart Resolver和FlashMapManager 。

SpringMVC中的handlerMappings對象怎么用

其中HandlerMapping是一個(gè)將請求與用于預(yù)處理和后處理的攔截器列表一起映射起來的處理程序,除了WebMvcConfigurationSupportXXX供用戶自定義的映射處理器之外,

主要有以下兩種內(nèi)置類型:

(1)RequestMappingHandlerMapping(它支持@RequestMapping修飾的方法)

(2)SimpleUrlHandlerMapping(它為處理程序維護(hù)URI路徑模式的顯式注冊)

SpringMVC中的handlerMappings對象怎么用

一.handlerMappings集合內(nèi)部的元素是什么,有什么作用?

通過官方文檔描述,handlerMappings集合內(nèi)部最主要有兩種映射處理器類型RequestMappingHandlerMapping和SimpleUrlHandlerMapping,它們分別用于處理不同類型的Controller。

我們知道可以通過注解@Controller或@RestController注解方式來聲明一個(gè)Controller,然后使用@RequestMapping來修飾類名或方法名,這種方式其實(shí)是由RequestMappingHandlerMapping映射處理器來對請求地址和方法名進(jìn)行映射的;那么SimpleUrlHandlerMapping又用于處理哪種方式的Controller呢,那就是采用實(shí)現(xiàn)Controller接口或?qū)崿F(xiàn)HttpRequestHandler接口的類,這種聲明Controller的方式是早期的做法現(xiàn)在在實(shí)際開發(fā)中用的比較少了但是也是SpringMVC所支持的。

對于RequestMappingHandlerMapping

這種類型的handlerMapping是我們通過@RequestMapping+請求地址來修飾方法時(shí),會由這么一個(gè)映射器來對這種配置方式進(jìn)行解析和映射處理,也就是說RequestMappingHandlerMapping類型的映射器是為注解聲明方式的映射專門設(shè)計(jì)的,<K,V>映射關(guān)系會存儲在這個(gè)handlerMapping的Map中。

SpringMVC中的handlerMappings對象怎么用

對于SimpleUrlHandlerMapping

這個(gè)映射器則是為繼承Controller類或?qū)崿F(xiàn)HttpRequestHandler接口的類進(jìn)行解析和映射處理的。

用代碼舉例子來驗(yàn)證,我們分別新建兩個(gè)類分別實(shí)現(xiàn)Controller接口和實(shí)現(xiàn)HttpRequestHandler接口,在類中寫請求接收方法,那么通過這兩種聲明的Controller通過請求地址在查詢請求方法時(shí)就會由SimpleUrlHandlerMapping進(jìn)行存儲和映射,<K,V>映射關(guān)系會存儲在這個(gè)SimpleUrlHandlerMapping的Map中。

(1)實(shí)現(xiàn)Controller接口的方式:

SpringMVC中的handlerMappings對象怎么用

(2)實(shí)現(xiàn)HttpRequestHandler接口的方式:

SpringMVC中的handlerMappings對象怎么用

然后重新進(jìn)行debug,使用http://localhost:9090/beanweb或http://localhost:9090/beanweb2請求,就會看到在handlerMapping中查找handler時(shí)會跳過第一種注解類型的RequestMappingHandlerMapping,而真正進(jìn)行解析處理的就是第二種非注解方式SimpleUrlHandlerMapping:

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

因此我們現(xiàn)在就明白了這個(gè)handlerMappings映射處理器集合的數(shù)據(jù)是怎么樣的,集合內(nèi)存儲的映射處理器分別處理怎么樣的請求。那么這個(gè)集合數(shù)據(jù)是怎么初始化的呢?

二.handlerMappings集合是怎么初始化的?

通過IDEAJ的Find Usages工具,在本文圖一的this.handlerMappings進(jìn)行調(diào)用搜索,可以一層層找到如下調(diào)用鏈:

HttpServletBean.init-->initServletBean()-->FrameworkServlet.initServletBean-->
FrameworkServlet.initWebApplicationContext-->FrameworkServlet.onRefresh
-->DispatcherServlet.onRefresh-->DispatcherServlet.initStrategies-->DispatcherServlet.initHandlerMappings
-->DispatcherServlet.getDefaultStrategies-->strategies.add((T) strategy)

通過前面SpringMVC前兩個(gè)流程的學(xué)習(xí)可知,DispatcherServlet繼承了FrameworkServlet,同時(shí)就集成了HttpServlet,也就是說DispatcherServlet本質(zhì)上就是一個(gè)Servlet,那么在容器加載這個(gè)Servlet的時(shí)候如果設(shè)置了<load-on-startup>加載時(shí)馬上進(jìn)行初始化,則在加載時(shí)會自動執(zhí)行其init方法,于是就有了上面調(diào)用鏈的第一個(gè)HttpServletBean.init入口。

我們的目的是找到哪里進(jìn)行了handlerMappings的核心操作,通過調(diào)用關(guān)系我們找到核心代碼在DispatcherServlet.initHandlerMappings:

SpringMVC中的handlerMappings對象怎么用

通過上面代碼返回的對象我們可以大膽推測matchingBeans就是我們要了解的handlerMappings集合,這個(gè)方法內(nèi)部完成了對多種類型HandlerMapping的初始化。通過查看我們重點(diǎn)關(guān)注的兩種映射器對象值,可以分別看到<K,V>關(guān)系:

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

上面就可以看到前面我們提到的urlLookup的Map集合以及handlerMap集合。為了了解這些不同的請求地址是怎么分配給不同類型的映射器來處理的,我們繼續(xù)點(diǎn)開這個(gè)核心方法:

SpringMVC中的handlerMappings對象怎么用

實(shí)際由下面這行代碼完成了:

lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit)

而真正值得思考的邏輯在于

String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

上面方法的邏輯就會自動生成一個(gè)有5種類型handlerMapping的handlerMappings,那么這5種映射器是怎么產(chǎn)生的呢?

通過核心代碼我們可以知道,這里通過傳入一個(gè)type="HandlerMapping"的類型,在beanDefinitionNames的集合中匹配所有屬于HandlerMapping類型的BeanName,然后放入到result數(shù)組返回,然后在單例池中根據(jù)BeanName獲取對應(yīng)的HandlerMapping類型的Bean。

也就是說這些HandlerMapping映射處理器的生成是通過在BeanDefinition中匹配,如果匹配上了從單例池中獲取對應(yīng)類型的Bean,因此最后的handlerMappings中就有這5種類型了,至于請求路徑和方法名怎么作為K,V綁定到對應(yīng)映射器上的,由于個(gè)人閱讀源碼的能力有限這部分代碼小編還尚未理解,作為小編的一個(gè)未解之謎吧。

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

當(dāng)然,既然5種類型的映射器是通過BD來生成的,那這些BeanDefinition又是什么時(shí)候被加到Spring容器中的呢?

這就不得不提一下關(guān)于HandlerMapping映射處理器的配置文件:DispatchServlet.properties文件,這個(gè)文件就定義了哪些內(nèi)置使用的組件聲明。

SpringMVC中的handlerMappings對象怎么用

SpringMVC中的handlerMappings對象怎么用

實(shí)際上,通過上面源碼的調(diào)試,對于HandlerAdaper的流程和HandlerMapping其實(shí)是很類似的。

總結(jié)

(1)handlerMappings映射器集合內(nèi)部一共有5個(gè)不同類型的映射器

分別是WebMvcConfigurationSupportXXX,RequestMappingHandlerMapping和BeanNameUrlHandlerMapping,RequestMappingHandlerMapping映射處理器用于使用注解方式請求地址和方法名進(jìn)行映射的Controller,SimpleUrlHandlerMapping用于實(shí)現(xiàn)Controller接口或?qū)崿F(xiàn)HttpRequestHandler接口的Controller;

(2)handlerMappings映射器集合初始化會經(jīng)歷一個(gè)調(diào)用鏈

在DispatchServlet啟動時(shí)自動開始初始化,

HttpServletBean.init-->initServletBean()-->FrameworkServlet.initServletBean--> FrameworkServlet.initWebApplicationContext-->FrameworkServlet.onRefresh -->DispatcherServlet.onRefresh-->DispatcherServlet.initStrategies-->DispatcherServlet.initHandlerMappings -->DispatcherServlet.getDefaultStrategies-->strategies.add((T) strategy)

初始化的結(jié)果是產(chǎn)生一個(gè)handlerMappings映射器集合,內(nèi)部包含5種不同類型的映射器,每種映射器內(nèi)部由Map<K,V>來維護(hù)一個(gè)<請求地址,全限定方法名>的映射關(guān)系;

(3)handlerMappings映射器集合的每個(gè)映射處理器

是在初始化時(shí)就生成了BeanDefinition,通過BeanDefinitionName和傳入的type=HandlerMapping類型在所有BeanDefinitionNames集合中匹配所有的映射處理器,再從單例池中獲取其對應(yīng)Bean實(shí)例放入handlerMappings中;

(4)對于HandlerAdaper的初始化和HandlerMapping流程是類似的

(5)<請求地址全限定方法名>的映射關(guān)系

在哪里生成并維護(hù)到映射處理器中的,這部分代碼小編暫時(shí)還沒能了解到。

三.handlerMappings有什么擴(kuò)展?

我們知道,SpringMVC中不能直接通過url來訪問WEB-INF下的靜態(tài)資源,如我們在工程webapp/jsp/新建一個(gè)1.jsp文件,通過

localhost:9090/jsp/1.jsp訪問時(shí)會報(bào)如下404錯(cuò)誤

SpringMVC中的handlerMappings對象怎么用

但是SpringBoot卻可以通過url直接訪問webapp,static,resource等包下面的靜態(tài)資源,為什么會這樣呢?原因就在于SpringBoot對SpringMVC的handlerMappings進(jìn)行了自己的拓展。

之所以SpringMVC不能訪問靜態(tài)資源是因?yàn)閷τ谶@種訪問靜態(tài)資源的請求,在handlerMappings內(nèi)沒有任何一個(gè)默認(rèn)的映射器可以進(jìn)行解析處理,因此自然這種請求就會被忽視。

對于SpringBoot來說,它自己實(shí)現(xiàn)了一個(gè)針對這種訪問靜態(tài)資源的映射處理器并交由handlerMappings來管理,因此就可以做到類似拓展,對于這個(gè)知識點(diǎn)在對SpringBoot學(xué)習(xí)時(shí)看能否研究一下這個(gè)特殊的映射處理器是怎么樣的,這里暫時(shí)知道這么一個(gè)結(jié)論即可。

感謝各位的閱讀!關(guān)于“SpringMVC中的handlerMappings對象怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI