HandlerMapping之RequestMappingHandlerMapping作用詳解
前言
上回我們知道HandlerMapping是用來尋找Handler的,并不與Handler的類型或者實現(xiàn)綁定,而是根據(jù)需要定義的。
那么為什么要單獨給@RequestMapping實現(xiàn)一個HandlerMapping?這次咱們就來專門看看這個RequestMappingHandlerMapping。
RequestMappingHandlerMapping
名字來源
因為RequestMappingHandlerMapping是專門為@RequestMapping而生的,因此他的名字是這樣來的:@RequestMapping的HandlerMapping了。
為什么不叫MethodHandlerMapping呢?
主要還是Handler是一個邏輯概念,MethodHandler了只是對目標方法進行了封裝,并不是真正處理請求的。
真正處理請求的是我們@RequestMapping的方法。取個名字都給你講道理。
@RequestMapping
在解答文章開頭的問題前,我們先看看@RequestMapping
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { /** * 處理器的名字,支持類級別和方法級別,多個路徑用#分割 */ String name() default ""; /** * 匹配請求路徑 */ @AliasFor("path") String[] value() default {}; /** * 匹配請求路徑 */ @AliasFor("value") String[] path() default {}; /** * Http請求方法:可選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ù)請求頭、請求方法、甚至是請求參數(shù)來匹配!
上回說的基于URL的兩種HandlerMapping都不能滿足他,因此必須推出一個更加強大、可擴展性更強的HandlerMapping——RequestMappingHandlerMapping
那么問題來了:
- @RequestMapping是在什么如何被解析的呢?
- 我們很容易想到的就是,遍歷容器中所有的對象,檢查是否存在@Controller注解。存在,那就是控制器。然后接著遍歷所有聲明的public方法,檢查是否存在@RequestMapping方法。這樣,我們就找到了處理器方法。
- @RequestMapping是在什么時候被解析的呢?
- 本著“誰使用,誰解析”的原則,他自然是被RequestMappingHandlerMapping解析的。而又因為@RequestMapping的尋找可太費功夫,不可能在提供映射服務時再來解析,只能是初始化時進行解析。因此實現(xiàn)InitializingBean進行初始化,是個選擇。
沒錯,實際上,SpringMVC跟你想的一樣。在InitializingBean的afterPropertiesSet方法中,完成了以3下件事:
- 尋找@Controller的bean,并找到所有的@RequestMapping方法
- 解析@RequestMapping封裝成RequestMappingInfo
- 將以上解析到的信息進行注冊。
信息包括:
信息 | 描述 |
@Controller/@RequestMapping對象 | 反射調(diào)用目標方法時,需要的target對象 |
RequestMappingInfo | 由@RequestMapping解析而來 |
@RequestMapping的方法 | 注冊時,注冊器會將Method與handler對象一起封裝成HandlerMethod進行注冊。便于后面適配器調(diào)用。 |
@RequestMapping的注冊
前面的解析@RequestMapping到RequestMappingInfo,可以省略,比較簡單。但是@RequestMapping的注冊沒辦法省略。因為如果搞不清楚他是怎么注冊的,也就沒辦法理解他是怎么尋找目標處理器的。
為了支撐@RequestMapping多樣化的匹配條件,不能再像前面兩款HanderMapping一樣,簡單粗暴的使用Map了。在 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping 的內(nèi)部定義了內(nèi)部類,專門用來做注冊中心,管理映射關系。
/** * Mapping注冊中心,可以理解為辦事處 */ class MappingRegistry { /** * T是匹配條件的對象。MappingRegistration是注冊的信息,可以理解為你要做事情。 * 對于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<>(); }
從他的屬性,我們可以分析得到如下信息:
- 直接通過請求路徑來查找處理器時,需要經(jīng)過pathLookup中轉registry,最后拿到MappingRegistration才到達HandlerMethod.
- 直接通過mappingName則可以直接找到HandlerMethod. 不過這個是Spring為了支持 <%@ taglib uri="//www.springframework.org/tags" prefix="s" %> 而提供的。跟我們平時使用沒多大關系。
但是各位觀眾老爺,pathLookup找到的是一個,而mappingName能找到多個,這是咋回事?誤會啊,pathLookup的value可不是簡單的一個元素,而是多個!他是MultiValueMap,不是我們經(jīng)??吹降牡財傌汬ashMap。他可以一個key對應多個value。
但是為什么會有多個呢?或者說為什么需要保存多個呢? 因為一個Handler可以處理多個請求,如果由多個Handler都能處理某一個請求的時候怎么辦呢?況且SpringMVC還支持通配符匹配。umm…這一幕有點似曾相識,我們的nginx做路由轉發(fā)的時候,不是也有類似的問題嗎?這意味著在查找Handler的時候,我們還需要找到最佳的匹配。例如,/*相較于/hello,那肯定是/hello更精確,更合適啦。你看,多嚴謹!
總結
由于@RequestMapping支持靈活的請求匹配條件,而不只是簡單的路徑,只能開發(fā)出RequestMappingHandlerMapping進行支持。
RequestMappingHandlerMapping是通過InitializingBean進行初始化的,在這里完成@RequestMappingHandlerMapping的掃描和解析,以及注冊。
HandlerMethod是在注冊時進行封裝的。獲取Handler時,拿到的Handler就是HandlerMethod。
后面的適配器適配的,也是他。
RequestMappingHandlerMapping使用了InitializingBean做初始化,但是當我們自己在做初始化的時候,尤其是使用多種初始化方式的時候,應當要注意Spring的調(diào)用順序,否則有可能發(fā)生NPE,或者獲取不到目標屬性的情況。
例如:同時在ApplicationContextAware、InitializingBean、@PostConstruct進行初始化。 為此,給大家找了官方的bean的生命周期
到此這篇關于HandlerMapping之RequestMappingHandlerMapping作用詳解的文章就介紹到這了,更多相關RequestMappingHandlerMapping作用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot如何通過自定義注解實現(xiàn)權限檢查詳解
這篇文章主要給大家介紹了關于SpringBoot如何通過自定義注解實現(xiàn)權限檢查的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10springboot2?使用activiti6?idea插件的過程詳解
這篇文章主要介紹了springboot2?使用activiti6?idea插件,本文通過截圖實例代碼相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03