背景
在 Spring MVC 中,DispatcherServlet 是前端控制器(front controller),它負責接收所有的 HTTP 請求并將它們映射到相應的處理器(handler)。為了實現這一點,Spring MVC 使用了適配器模式將 Controller 與 DispatcherServlet 綁定在一起。
在Spring MVC的優雅設計中,所有公開的接口默認都通過RequestMappingHandlerMapping進行映射轉換。這一過程的核心在于如何將這些接口有效地注冊到RequestMappingHandlerMapping。本文將深入探討這一機制,揭開其背后的原理和細節,這是我們研究的主要焦點。
RequestMappingHandlerMapping介紹
RequestMappingHandlerMapping 是 Spring MVC 中的一個類,用于將請求映射到處理器方法。它是 AbstractHandlerMethodMapping 的一個具體實現,提供了一些默認的請求映射策略。
在 Spring MVC 中,HandlerMapping 負責將請求映射到相應的處理器方法。RequestMappingHandlerMapping 提供了一個基本的框架,可以自定義擴展以支持不同的請求映射方式。例如,可以通過繼承RequestMappingHandlerMapping 并重寫其中的方法來實現自定義的請求映射策略。
具體來說,RequestMappingHandlerMapping 主要包含以下幾個關鍵部分:
-
registerHandlerMethod 方法:該方法用于注冊一個處理器方法。它首先檢查該處理器方法是否已經注冊過,如果沒有則將其添加到內部維護的處理器方法列表中。
-
getHandlerInternal 方法:該方法根據請求信息獲取對應的處理器方法。它首先通過lookupHandlerMethod 方法查找匹配的處理器方法,然后通過 instantiateHandlerMethod 方法實例化處理器方法對象。
-
lookupHandlerMethod 方法:該方法根據請求信息查找匹配的處理器方法。它首先通過extractPathWithinApplication 方法提取請求路徑中的應用程序路徑,然后通過 matches 方法匹配處理器方法。如果找到匹配的處理器方法,則返回該處理器方法;否則返回 null。
-
matches 數組:該方法根據請求信息和處理器方法進行匹配。它首先檢查請求路徑是否與處理器方法的 URL 模式匹配,然后檢查請求方法是否與處理器方法的 HTTP 方法匹配。如果兩個條件都滿足,則認為匹配成功。
-
handleMatch 方法:該方法處理匹配成功的處理器方法。它首先調用 preHandle 方法進行預處理,然后調用處理器方法執行業務邏輯,最后調用 afterCompletion 方法進行后處理。
注冊過程
AbstractHandlerMethodMapping是Spring MVC中用于處理請求映射的抽象類。它提供了一些基本的方法,如獲取處理器方法、處理方法參數等。具體的實現類需要繼承這個抽象類并實現相應的方法。
AbstractHandlerMethodMapping 中的 detectHandlerMethods 方法是用于從處理器中獲取處理器方法并注冊的。這個方法是一個受保護的方法,它的作用是檢測帶有特定注解(如@RequestMapping)的方法,并將這些方法注冊到映射器中,以便后續可以根據請求找到對應的處理器方法來處理請求。
具體來說,detectHandlerMethods 方法會執行以下步驟:
- 獲取handler的類型:如果傳入的handler是字符串類型,則將其轉換為對應的類類型。
Class<?> handlerType = (handler instanceof String ?obtainApplicationContext().getType((String) handler) : handler.getClass());
- 檢測handler的方法:遍歷handler的所有方法,檢測哪些方法帶有特定的注解(如@RequestMapping),這些方法被視為處理器方法。
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {try {return getMappingForMethod(method, userType);}catch (Throwable ex) {throw new IllegalStateException("Invalid mapping on handler class [" +userType.getName() + "]: " + method, ex);}});
- 注冊處理器方法:將檢測到的處理器方法注冊到映射器中,這樣當接收到請求時,映射器就可以根據請求的信息找到對應的處理器方法來處理請求。
methods.forEach((method, mapping) -> {Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}
調用過程
在 Spring MVC 中,RequestMappingHandlerMapping 是負責處理基于注解的控制器方法的映射。默認情況下,所有標記有 @RequestMapping 注解的控制器方法都會通過 RequestMappingHandlerMapping 進行注冊和處理。這個過程涉及到以下幾個關鍵步驟:
- Spring容器啟動:
- 在應用啟動時,Spring 容器會初始化所有的單例 Bean,包括 DispatcherServlet 和相關的組件。
- 初始化 RequestMappingHandlerMapping:
- RequestMappingHandlerMapping 實現了 InitializingBean 接口,因此它的 afterPropertiesSet() 方法會在所有屬性設置完成后被調用,以完成其初始化工作。
- 掃描控制器組件:
- 在初始化過程中,RequestMappingHandlerMapping 會掃描 Spring 容器中的 Bean,尋找帶有 @Controller 注解的類以及帶有 @RequestMapping 注解的方法。
- 注冊映射關系:
- 對于找到的控制器和方法,RequestMappingHandlerMapping 會將它們的 URL 路徑和處理方法之間的映射關系注冊到內部的映射注冊表中。
- 構建URL到方法的映射:
- RequestMappingHandlerMapping 會解析這些映射信息,構建一個從 URL 到控制器方法的映射表,以便能夠快速地根據請求的 URL 找到對應的處理方法。
- 處理請求:
- 當 HTTP 請求到達 DispatcherServlet 時,它會使用 RequestMappingHandlerMapping 來確定請求應該由哪個控制器方法來處理。一旦找到匹配的方法,DispatcherServlet 會使用 RequestMappingHandlerAdapter 來執行該方法。
- 適配器模式的應用:
- 適配器模式在這里確保了 DispatcherServlet 能夠通過統一的 HandlerAdapter 接口來執行不同類型的處理器,而不需要了解具體的實現細節。