HandlerMapping之RequestMappingHandlerMapping作用詳解
前言
上回我們知道HandlerMapping是用來(lái)尋找Handler的,并不與Handler的類型或者實(shí)現(xiàn)綁定,而是根據(jù)需要定義的。
那么為什么要單獨(dú)給@RequestMapping實(shí)現(xiàn)一個(gè)HandlerMapping?這次咱們就來(lái)專門看看這個(gè)RequestMappingHandlerMapping。
RequestMappingHandlerMapping
名字來(lái)源
因?yàn)镽equestMappingHandlerMapping是專門為@RequestMapping而生的,因此他的名字是這樣來(lái)的:@RequestMapping的HandlerMapping了。
為什么不叫MethodHandlerMapping呢?
主要還是Handler是一個(gè)邏輯概念,MethodHandler了只是對(duì)目標(biāo)方法進(jìn)行了封裝,并不是真正處理請(qǐng)求的。
真正處理請(qǐng)求的是我們@RequestMapping的方法。取個(gè)名字都給你講道理。
@RequestMapping
在解答文章開頭的問(wèn)題前,我們先看看@RequestMapping
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
/**
* 處理器的名字,支持類級(jí)別和方法級(jí)別,多個(gè)路徑用#分割
*/
String name() default "";
/**
* 匹配請(qǐng)求路徑
*/
@AliasFor("path")
String[] value() default {};
/**
* 匹配請(qǐng)求路徑
*/
@AliasFor("value")
String[] path() default {};
/**
* Http請(qǐng)求方法:可選GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE
*/
RequestMethod[] method() default {};
/**
* 匹配指定的地址欄參數(shù)
*/
String[] params() default {};
/**
* 匹配特定的header
*/
String[] headers() default {};
/**
* 匹配特定的Content-Type
*/
String[] consumes() default {};
/**
* 匹配特定的Accept
*/
String[] produces() default {};
}發(fā)現(xiàn)了嗎,各位?他除了能根據(jù)URI匹配,還能根據(jù)請(qǐng)求頭、請(qǐng)求方法、甚至是請(qǐng)求參數(shù)來(lái)匹配!
上回說(shuō)的基于URL的兩種HandlerMapping都不能滿足他,因此必須推出一個(gè)更加強(qiáng)大、可擴(kuò)展性更強(qiáng)的HandlerMapping——RequestMappingHandlerMapping

那么問(wèn)題來(lái)了:
- @RequestMapping是在什么如何被解析的呢?
- 我們很容易想到的就是,遍歷容器中所有的對(duì)象,檢查是否存在@Controller注解。存在,那就是控制器。然后接著遍歷所有聲明的public方法,檢查是否存在@RequestMapping方法。這樣,我們就找到了處理器方法。
- @RequestMapping是在什么時(shí)候被解析的呢?
- 本著“誰(shuí)使用,誰(shuí)解析”的原則,他自然是被RequestMappingHandlerMapping解析的。而又因?yàn)锧RequestMapping的尋找可太費(fèi)功夫,不可能在提供映射服務(wù)時(shí)再來(lái)解析,只能是初始化時(shí)進(jìn)行解析。因此實(shí)現(xiàn)InitializingBean進(jìn)行初始化,是個(gè)選擇。
沒(méi)錯(cuò),實(shí)際上,SpringMVC跟你想的一樣。在InitializingBean的afterPropertiesSet方法中,完成了以3下件事:
- 尋找@Controller的bean,并找到所有的@RequestMapping方法
- 解析@RequestMapping封裝成RequestMappingInfo
- 將以上解析到的信息進(jìn)行注冊(cè)。
信息包括:
| 信息 | 描述 |
| @Controller/@RequestMapping對(duì)象 | 反射調(diào)用目標(biāo)方法時(shí),需要的target對(duì)象 |
| RequestMappingInfo | 由@RequestMapping解析而來(lái) |
| @RequestMapping的方法 | 注冊(cè)時(shí),注冊(cè)器會(huì)將Method與handler對(duì)象一起封裝成HandlerMethod進(jìn)行注冊(cè)。便于后面適配器調(diào)用。 |
@RequestMapping的注冊(cè)
前面的解析@RequestMapping到RequestMappingInfo,可以省略,比較簡(jiǎn)單。但是@RequestMapping的注冊(cè)沒(méi)辦法省略。因?yàn)槿绻悴磺宄窃趺醋?cè)的,也就沒(méi)辦法理解他是怎么尋找目標(biāo)處理器的。
為了支撐@RequestMapping多樣化的匹配條件,不能再像前面兩款HanderMapping一樣,簡(jiǎn)單粗暴的使用Map了。在 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 的內(nèi)部定義了內(nèi)部類,專門用來(lái)做注冊(cè)中心,管理映射關(guān)系。
/**
* Mapping注冊(cè)中心,可以理解為辦事處
*/
class MappingRegistry {
/**
* T是匹配條件的對(duì)象。MappingRegistration是注冊(cè)的信息,可以理解為你要做事情。
* 對(duì)于RequestMappingHandlerMapping,T就是RequestMappingInfo
* MappingRegistration包括信息:RequestMappingInfo、HandlerMethod、directPaths、mappingName、corsConfig
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* Map<path, RequestMappingInfo>
*/
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
/**
* Map<mappingName, List<HandlerMethod>>
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
}從他的屬性,我們可以分析得到如下信息:
- 直接通過(guò)請(qǐng)求路徑來(lái)查找處理器時(shí),需要經(jīng)過(guò)pathLookup中轉(zhuǎn)registry,最后拿到MappingRegistration才到達(dá)HandlerMethod.
- 直接通過(guò)mappingName則可以直接找到HandlerMethod. 不過(guò)這個(gè)是Spring為了支持 <%@ taglib uri="//www.springframework.org/tags" prefix="s" %> 而提供的。跟我們平時(shí)使用沒(méi)多大關(guān)系。
但是各位觀眾老爺,pathLookup找到的是一個(gè),而mappingName能找到多個(gè),這是咋回事?誤會(huì)啊,pathLookup的value可不是簡(jiǎn)單的一個(gè)元素,而是多個(gè)!他是MultiValueMap,不是我們經(jīng)常看到的地?cái)傌汬ashMap。他可以一個(gè)key對(duì)應(yīng)多個(gè)value。
但是為什么會(huì)有多個(gè)呢?或者說(shuō)為什么需要保存多個(gè)呢? 因?yàn)橐粋€(gè)Handler可以處理多個(gè)請(qǐng)求,如果由多個(gè)Handler都能處理某一個(gè)請(qǐng)求的時(shí)候怎么辦呢?況且SpringMVC還支持通配符匹配。umm…這一幕有點(diǎn)似曾相識(shí),我們的nginx做路由轉(zhuǎn)發(fā)的時(shí)候,不是也有類似的問(wèn)題嗎?這意味著在查找Handler的時(shí)候,我們還需要找到最佳的匹配。例如,/*相較于/hello,那肯定是/hello更精確,更合適啦。你看,多嚴(yán)謹(jǐn)!
總結(jié)
由于@RequestMapping支持靈活的請(qǐng)求匹配條件,而不只是簡(jiǎn)單的路徑,只能開發(fā)出RequestMappingHandlerMapping進(jìn)行支持。
RequestMappingHandlerMapping是通過(guò)InitializingBean進(jìn)行初始化的,在這里完成@RequestMappingHandlerMapping的掃描和解析,以及注冊(cè)。
HandlerMethod是在注冊(cè)時(shí)進(jìn)行封裝的。獲取Handler時(shí),拿到的Handler就是HandlerMethod。
后面的適配器適配的,也是他。
RequestMappingHandlerMapping使用了InitializingBean做初始化,但是當(dāng)我們自己在做初始化的時(shí)候,尤其是使用多種初始化方式的時(shí)候,應(yīng)當(dāng)要注意Spring的調(diào)用順序,否則有可能發(fā)生NPE,或者獲取不到目標(biāo)屬性的情況。
例如:同時(shí)在ApplicationContextAware、InitializingBean、@PostConstruct進(jìn)行初始化。 為此,給大家找了官方的bean的生命周期

到此這篇關(guān)于HandlerMapping之RequestMappingHandlerMapping作用詳解的文章就介紹到這了,更多相關(guān)RequestMappingHandlerMapping作用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中資源加載的方法及Spring的ResourceLoader應(yīng)用小結(jié)
在Java開發(fā)中,資源加載是一個(gè)基礎(chǔ)而重要的操作,這篇文章主要介紹了深入理解Java中資源加載的方法及Spring的ResourceLoader應(yīng)用,本文通過(guò)實(shí)例代碼演示了通過(guò)ClassLoader和Class獲取資源的內(nèi)容,以及使用Spring的ResourceLoader加載多個(gè)資源的過(guò)程,需要的朋友可以參考下2024-01-01
SpringBoot中@Transiactional注解沒(méi)有效果的解決
這篇文章主要介紹了SpringBoot中@Transiactional注解沒(méi)有效果的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java使用Predicate過(guò)濾集合中的元素的操作方法
文章介紹了Java 8中Collection接口新增的removeIf方法,該方法允許通過(guò)Predicate條件快速移除集合中不符合條件的元素,與傳統(tǒng)使用Iterator和remove()方法相比,removeIf方法更加簡(jiǎn)潔和易讀,文章還提供了使用removeIf方法的示例,需要的朋友可以參考下2025-11-11
spring如何快速穩(wěn)定解決循環(huán)依賴問(wèn)題
這篇文章主要介紹了spring如何快速穩(wěn)定解決循環(huán)依賴問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Mybatis實(shí)現(xiàn)Mapper動(dòng)態(tài)代理方式詳解
這篇文章主要為大家詳細(xì)介紹了Mybatis實(shí)現(xiàn)Mapper動(dòng)態(tài)代理方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Ubuntu安裝jenkins完成自動(dòng)化構(gòu)建詳細(xì)步驟
Jenkins是一個(gè)開源的自動(dòng)化服務(wù)器,可以用來(lái)輕松地建立持續(xù)集成和持續(xù)交付(CI/CD)管道,這篇文章主要給大家介紹了關(guān)于Ubuntu安裝jenkins完成自動(dòng)化構(gòu)建的相關(guān)資料,需要的朋友可以參考下2024-03-03

