1. 程式人生 > >精盡Spring MVC原始碼分析 - HandlerMapping 元件(四)之 AbstractUrlHandlerMapping

精盡Spring MVC原始碼分析 - HandlerMapping 元件(四)之 AbstractUrlHandlerMapping

> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀 > > Spring 版本:5.2.4.RELEASE > > 該系列其他文件請檢視:[**《精盡 Spring MVC 原始碼分析 - 文章導讀》**](https://www.cnblogs.com/lifullmoon/p/14123963.html) ## HandlerMapping 元件 HandlerMapping 元件,請求的**處理器匹配器**,負責為請求找到合適的 `HandlerExecutionChain` 處理器執行鏈,包含處理器(`handler`)和攔截器們(`interceptors`) - `handler` 處理器是 Object 型別,可以將其理解成 HandlerMethod 物件(例如我們使用最多的 `@RequestMapping` 註解所標註的方法會解析成該物件),包含了方法的所有資訊,通過該物件能夠執行該方法 - `HandlerInterceptor` 攔截器對處理請求進行**增強處理**,可用於在執行方法前、成功執行方法後、處理完成後進行一些邏輯處理 由於 HandlerMapping 元件涉及到的內容比較多,考慮到內容的排版,所以將這部分內容拆分成了四個模組,依次進行分析: - [**《HandlerMapping 元件(一)之 AbstractHandlerMapping》**](https://www.cnblogs.com/lifullmoon/p/14137308.html) - [**《HandlerMapping 元件(二)之 HandlerInterceptor 攔截器》**](https://www.cnblogs.com/lifullmoon/p/14137358.html) - [**《HandlerMapping 元件(三)之 AbstractHandlerMethodMapping》**](https://www.cnblogs.com/lifullmoon/p/14137380.html) - [**《HandlerMapping 元件(四)之 AbstractUrlHandlerMapping》**](https://www.cnblogs.com/lifullmoon/p/14137390.html) ## HandlerMapping 元件(四)之 AbstractUrlHandlerMapping 先來回顧一下HandlerMapping 介面體系的結構:
在[**《HandlerMapping 元件(一)之 AbstractHandlerMapping》**](https://www.cnblogs.com/lifullmoon/p/14137308.html)文件中已經分析了 HandlerMapping 元件的 **AbstractHandlerMapping** 抽象類基類 在[**《HandlerMapping 元件(三)之 AbstractHandlerMethodMapping》**](https://www.cnblogs.com/lifullmoon/p/14137380.html)文件中也已經分析了圖中**紅色框**部分的 **AbstractHandlerMethodMapping** 系,基於 Method 進行匹配。例如,我們所熟知的 @RequestMapping 等註解的方式。 那麼本文就接著來分析圖中**黃色框**部分的 **AbstractUrlHandlerMapping** 系,基於 URL 進行匹配。例如 [《基於 XML 配置的 Spring MVC 簡單的 HelloWorld 例項應用》](https://www.cnblogs.com/liuhongfeng/p/4769076.html) ,當然,目前這種方式已經基本不用了,被 `@RequestMapping` 等註解的方式所取代。不過,Spring MVC 內建的一些路徑匹配,還是使用這種方式。 >
因為 AbstractUrlHandlerMapping 在實際開發基本不會涉及到,所以本文**選讀**,可以直接檢視**總結**部分 一共有五個子類,分成兩條線: - AbstractUrlHandlerMapping <= SimpleUrlHandlerMapping <= WebSocketHandlerMapping - AbstractUrlHandlerMapping <= AbstractDetectingUrlHandlerMapping <= BeanNameUrlHandlerMapping 其中,WebSocketHandlerMapping 是 `spring-websocket` 專案中的類,本文會無視它 所以,本文按照 AbstractUrlHandlerMapping、SimpleUrlHandlerMapping、AbstractDetectingUrlHandlerMapping、BeanNameUrlHandlerMapping 順序進行分析 ### 回顧 先來回顧一下在 `DispatcherServlet` 中處理請求的過程中通過 HandlerMapping 元件,獲取到 HandlerExecutionChain 處理器執行鏈的方法,是通過AbstractHandlerMapping 的 getHandler 方法來獲取的,如下: ```java @Override @Nullable public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // <1> 獲得處理器(HandlerMethod 或者 HandlerExecutionChain),該方法是抽象方法,由子類實現 Object handler = getHandlerInternal(request); // <2> 獲得不到,則使用預設處理器 // <3> 還是獲得不到,則返回 null // <4> 如果找到的處理器是 String 型別,則從 Spring 容器中找到對應的 Bean 作為處理器 // <5> 建立 HandlerExecutionChain 物件(包含處理器和攔截器) // ... 省略相關程式碼 return executionChain; } ``` 在 AbstractHandlerMapping 獲取 HandlerExecutionChain 處理器執行鏈的方法中,需要先呼叫 `getHandlerInternal(HttpServletRequest request)` 抽象方法,獲取請求對應的處理器,該方法由子類去實現,也就上圖中**黃色框**和**紅色框**兩類子類,本文分析**黃色框**部分內容 ### AbstractUrlHandlerMapping `org.springframework.web.servlet.handler.AbstractUrlHandlerMapping`,實現 MatchableHandlerMapping 介面,繼承 AbstractHandlerMapping 抽象類,以 **URL** 作為 **Handler 處理器** 的 HandlerMapping 抽象類,提供 Handler 的獲取、註冊等等通用的骨架方法。 #### 構造方法 ```java public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping { /** * 根路徑("/")的處理器 */ @Nullable private Object rootHandler; /** * 使用後置的 / 匹配 */ private boolean useTrailingSlashMatch = false; /** * 是否延遲載入處理器,預設關閉 */ private boolean lazyInitHandlers = false; /** * 路徑和處理器的對映 * * KEY:路徑 {@link #lookupHandler(String, HttpServletRequest)} */ private final Map handlerMap = new LinkedHashMap<>(); } ``` #### registerHandler `registerHandler(String[] urlPaths, String beanName)` 方法,註冊多個 URL 的處理器,方法如下: ```java protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. // <1> 如果非延遲載入,並且 handler 為 String 型別,並且還是單例,則去獲取 String 對應的 Bean 物件 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } // <2> 獲得 urlPath 對應的處理器 Object mappedHandler = this.handlerMap.get(urlPath); // <3> 檢驗 mappedHandler 是否已存在,如果已存在,並且不是當前 resolvedHandler 物件,則丟擲異常 if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { // <4.1> 如果是 / 根路徑,則設定為 rootHandler if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler); } // <4.2> 如果是 /* 路徑,則設定為預設處理器 else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler); } // <4.3> 新增到 handlerMap 中 else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } } ``` 遍歷 URL,依次註冊處理器 1. 如果非延遲載入,並且 `handler` 為 String 型別,並且還是單例,則去獲取 String 對應的 Bean 物件,`resolvedHandler ` 2. 從`handlerMap`中獲得 `urlPath` 對應的處理器 3. 如果該路徑已存在對應的處理器,但是不是當前 `resolvedHandler` 物件,則丟擲異常 4. 否則,該路徑不存在對應的處理器,則將當前 `resolvedHandler` 處理器儲存 1. 如果是 `/` 根路徑,則設定 `resolvedHandler` 為 `rootHandler` 2. 否則,如果是 `/*` 路徑,則設定為預設處理器 `defaultHandler`(在父類中) 3. 否則,新增到 `handlerMap` 中 #### getHandlerInternal 實現父類的 `getHandlerInternal(HttpServletRequest request)` 方法,獲得處理器,方法如下: ```java @Override @Nullable protected Object getHandlerInternal(HttpServletRequest request) throws Exception { // <1> 獲得請求的路徑 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); // <2> 獲得處理器 Object handler = lookupHandler(lookupPath, request); // <3> 如果找不到處理器,則使用 rootHandler 或 defaultHandler 處理器 if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; // <3.1> 如果是根路徑,則使用 rootHandler 處理器 if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } // <3.2> 使用預設處理器 if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? // <3.3> 如果找到的處理器是 String 型別,則從容器中找到該 beanName 對應的 Bean 作為處理器 if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = obtainApplicationContext().getBean(handlerName); } // <3.4> 空方法,校驗處理器。目前暫無子類實現該方法 validateHandler(rawHandler, request); // <3.5> 建立處理器(HandlerExecutionChain 物件) handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; } ``` 1. 獲得請求路徑 2. 呼叫 `lookupHandler(String urlPath, HttpServletRequest request)` 方法,獲得處理器,詳情見下文 3. 如果找不到處理器,則使用 `rootHandler` 或 `defaultHandler` 處理器 1. 如果是`/`根路徑,則使用 `rootHandler` 處理器 2. 否則,使用 `defaultHandler` 預設處理器 3. 如果找到的處理器是 String 型別,則從容器中找到該 beanName 對應的 Bean 作為處理器 4. 呼叫`validateHandler(Object handler, HttpServletRequest request)`,對處理器進行校驗,空方法,暫無子類實現該方法 5. 呼叫 `buildPathExposingHandler`方法,建立 HandlerExecutionChain 處理器執行鏈,賦值給`handler`處理器,詳情見下文 4. 返回請求對應的`handler`處理器 所以說這裡但會的處理器物件可能是一個 HandlerExecutionChain 物件,用途目前不清楚