首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring MVC 核心源碼深度解析:HandlerMapping 請求映射與查找全流程

     編輯:Java 綜合教學

Spring MVC 核心源碼深度解析:HandlerMapping 請求映射與查找全流程

學習框架時,我習慣深入源碼探究類之間的協作關係與方法調用鏈路,如此方能踏實掌握。本文以 SimpleUrlHandlerMapping 為切入點,剖析 HandlerMapping 如何根據請求 URL 定位到對應的 Controller。

HandlerMapping 核心職責

解析請求 URL,根據映射規則找到處理該請求的 Handler(即我們編寫的 Controller)。

繼承體系概覽

HandlerMapping (介面) → AbstractHandlerMapping → AbstractUrlHandlerMapping → SimpleUrlHandlerMapping / BeanNameUrlHandlerMapping 等實現類。
不同實現類的映射策略不同,但最終目標一致:URL → Handler。

HandlerMapping 繼承體系圖 詳細繼承關係圖

兩大核心階段:註冊與查找

  • 註冊階段: 容器啟動時,解析配置文件,將 URL 模式與 Controller Bean 以 <key, value> 形式存入 Map(handlerMap)。
  • 查找階段: 請求到達時,根據 URL 從 handlerMap 取出 Handler,封裝為 HandlerExecutionChain(包含 Handler 與攔截器鏈)返回。

階段一:註冊流程詳解

1. 配置範例

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/*.do">helloWorld</prop>
        </props>
    </property>
</bean>
<bean id="helloWorld" class="cn.controller.HelloController" />

2. 關鍵源碼鏈路

  1. SimpleUrlHandlerMapping.initApplicationContext() → 調用父類 registerHandlers(urlMap)。
  2. AbstractUrlHandlerMapping.registerHandlers(Map):遍歷 urlMap,補全 URL 前綴 /、去除 Bean 名稱空白,逐個調用 registerHandler(url, handler)。
  3. AbstractUrlHandlerMapping.registerHandler(String urlPath, Object handler)(核心):
    • 若 handler 為 Bean 名稱 (String) 且非懶加載,從 IOC 容器獲取真實 Bean 實例。
    • 檢測衝突:同一 URL 不能映射到不同 Handler。
    • 特殊路徑處理:/ → setRootHandler,/* → setDefaultHandler,其餘 → handlerMap.put(urlPath, resolvedHandler)。

AbstractHandlerMapping 類結構 ApplicationObjectSupport 類結構

階段二:查找流程詳解

1. DispatcherServlet 初始化 HandlerMapping 列表

protected void initStrategies(ApplicationContext context) {
    // ...
    initHandlerMappings(context); // 從容器獲取所有 HandlerMapping Bean,排序後存入 this.handlerMappings
}

2. 請求處理入口 doDispatch()

HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);

3. DispatcherServlet.getHandler() 遍歷所有 HandlerMapping

for (HandlerMapping hm : this.handlerMappings) {
    HandlerExecutionChain handler = hm.getHandler(request);
    if (handler != null) return handler;
}
return null;

4. AbstractHandlerMapping.getHandler() (模板方法)

  • 調用抽象方法 getHandlerInternal(request) 獲取原始 Handler。
  • 若為空,嘗試獲取默認 Handler。
  • 將 Handler 封裝為 HandlerExecutionChain,合併攔截器、處理 CORS 等。

5. AbstractUrlHandlerMapping.getHandlerInternal() 具體實現

  1. lookupPath = getUrlPathHelper().getLookupPathForRequest(request):提取請求中用於匹配的路徑片段。
  2. handler = lookupHandler(lookupPath, request):核心匹配邏輯。
    • 優先精確匹配 handlerMap.get(urlPath)。
    • 失敗則進行 Ant 風格模式匹配 (PathMatcher),選擇最精確模式(最長路徑、最少通配符)。
    • 提取 URI 模板變量、矩陣變量,構建最終 Handler。

總結:從 URL 到 Controller 的完整鏈路

  1. SimpleUrlHandlerMapping 解析配置,生成 {urlPattern=beanName} Map,調用父類 registerHandler。
  2. AbstractUrlHandlerMapping.registerHandler 解析 Bean 名稱獲取實例,存入 handlerMap (urlPath → Controller)。
  3. DispatcherServlet 啟動時收集所有 HandlerMapping 實例。
  4. 請求到達,DispatcherServlet 遍歷 HandlerMapping 調用 getHandler。
  5. AbstractHandlerMapping.getHandler → getHandlerInternal 截取路徑 → lookupHandler 在 handlerMap 中查找 → 封裝 HandlerExecutionChain 返回。

理解這一流程,對排查「404 無法映射」、「多 Handler 衝突」、「攔截器不生效」等問題極有幫助。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved