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

Spring MVC 源碼深度解析:HandlerAdapter 初始化全流程

     編輯:Java 綜合教學

Spring MVC 源碼深度解析:HandlerAdapter 初始化全流程

RequestMappingHandlerAdapter 是 Spring MVC 中處理 @RequestMapping 註解請求的核心適配器。其初始化過程(afterPropertiesSet())完成四大關鍵任務:引數解析器註冊、返回值處理器註冊、資料綁定解析器註冊、全域 @ControllerAdvice 緩存建立。本文將深入源碼,剖析每一環節的設計細節與實現機制。

一、引數解析器初始化與責任鏈模式

HandlerMethodArgumentResolver 介面定義了 supportsParameter() 與 resolveArgument() 兩個方法,分別判斷是否支援某參數及實際解析邏輯。RequestMappingHandlerAdapter 在 getDefaultArgumentResolvers() 中按四大類別裝配 24+ 內建解析器:

  • 基於註解:@RequestParam、@PathVariable、@MatrixVariable、@ModelAttribute、@RequestBody、@RequestPart、@RequestHeader、@CookieValue、@Value
  • 基於類型:ServletRequest、ServletResponse、HttpEntity、RedirectAttributes、Model、Map、Errors、SessionStatus、UriComponentsBuilder
  • 自定義:使用者透過 getCustomArgumentResolvers() 注入
  • 兜底通用:@RequestParam(required=true)、ServletModelAttributeMethodProcessor(綁定所有簡單類型)

Composite 封裝與責任鏈迭代

所有解析器被封裝進 HandlerMethodArgumentResolverComposite,其 getArgumentResolver() 實現標準責任鏈模式:

private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    if (result == null) {
        for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
            if (resolver.supportsParameter(parameter)) {
                result = resolver;
                this.argumentResolverCache.put(parameter, result);
                break; // 找到第一個支援的即停止
            }
        }
    }
    return result;
}

緩存機制確保同一參數只需遍歷一次,後續直接命中。順序至關重要:越具體的解析器(如 @RequestParam)越靠前,通用兜底解析器置於最後,避免誤捕獲。

二、@InitBinder 解析器:資料綁定專用

@InitBinder 方法用於註冊 PropertyEditor、Converter、Validator 等。其解析器初始化邏輯(getDefaultInitBinderArgumentResolvers())與一般引數解析器類似,但移除了 @ModelAttribute、@RequestBody、@RequestPart、@CookieValue、Map、Model、Errors、SessionStatus 等與資料綁定無關的解析器,僅保留 @RequestParam、@PathVariable、@MatrixVariable、@Value、ServletRequest/Response 及自定義解析器。這體現了「最小權限原則」:資料綁定階段只解析真正需要的參數類型。

三、返回值處理器:從處理器結果到 ModelAndView

HandlerMethodReturnValueHandler 介面同樣定義 supportsReturnType() 與 handleReturnValue()。getDefaultReturnValueHandlers() 裝配四類處理器:

  • 單一用途:ModelAndView、Model、View、HttpEntity、Callable、DeferredResult、AsyncTask
  • 基於註解:@ModelAttribute、@ResponseBody
  • 多用途:ViewName(String)、Map
  • 自定義與兜底:自定義處理器、ModelAndViewResolver、最終兜底 ModelAttributeMethodProcessor(true)

順序同樣關鍵:ModelAndView 最優先,String 視圖名與 Map 置於中段,兜底處理器最後。異步返回類型(Callable 等)專門處理非阻塞流程。

四、@ControllerAdvice 全域緩存建立

@ControllerAdvice 解決跨控制器共享 @ModelAttribute、@InitBinder、@ExceptionHandler 的需求,避免單繼承限制。初始化流程(initControllerAdviceCache()):

  1. 掃描容器所有 Bean,透過 applicationContext.getBeanDefinitionNames() 獲取名稱,findAnnotationOnBean(name, ControllerAdvice.class) 過濾帶註解的 Bean。
  2. 封裝為 ControllerAdviceBean,按 @Order 或 Ordered 介面排序。
  3. 使用 HandlerMethodSelector.selectMethods() 提取該 Bean 中標註 @ModelAttribute 與 @InitBinder 的方法。
  4. 分別存入 modelAttributeAdviceCache、initBinderAdviceCache,Key 為 ControllerAdviceBean,Value 為方法集合。

ControllerAdviceBean 核心實現

public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
    List<ControllerAdviceBean> beans = new ArrayList<>();
    for (String name : context.getBeanDefinitionNames()) {
        if (context.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
            beans.add(new ControllerAdviceBean(name, context));
        }
    }
    return beans;
}

// 支援 Bean 名稱或直接物件,實作 Ordered 介面決定優先順序
private static int initOrderFromBeanType(Class<?> beanType) {
    Order annot = AnnotationUtils.findAnnotation(beanType, Order.class);
    return (annot != null) ? annot.value() : Ordered.LOWEST_PRECEDENCE;
}

執行階段,RequestMappingHandlerAdapter 會優先檢查全域緩存,再檢查控制器本地方法,實現「全域增強、局部覆蓋」的語義。

總結:模組化、可擴展的初始化架構

RequestMappingHandlerAdapter 初始化展現了 Spring MVC 的核心設計哲學:

  • 介面抽象:HandlerMethodArgumentResolver、HandlerMethodReturnValueHandler 定義標準契約
  • 組合優於繼承:Composite 模式聚合多種策略,責任鏈動態分派
  • 約定優於配置:內建 24+ 解析器覆蓋 95% 場景,自定義擴展無侵入
  • 緩存優化:參數/返回類型解析結果緩存,避免重複遍歷
  • 全域與局部分離:@ControllerAdvice 掃描緩存機制,支援跨控制器關注點分離

深入理解這套初始化機制,不僅能排查參數綁定、返回值處理、全域增強失效等疑難雜症,更能在需要自定義引數解析、返回值封裝、跨切面邏輯時,精準切入擴展點,寫出符合 Spring 精神的優雅程式碼。

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