RequestMappingHandlerAdapter 是 Spring MVC 中處理 @RequestMapping 註解請求的核心適配器。其初始化過程(afterPropertiesSet())完成四大關鍵任務:引數解析器註冊、返回值處理器註冊、資料綁定解析器註冊、全域 @ControllerAdvice 緩存建立。本文將深入源碼,剖析每一環節的設計細節與實現機制。
HandlerMethodArgumentResolver 介面定義了 supportsParameter() 與 resolveArgument() 兩個方法,分別判斷是否支援某參數及實際解析邏輯。RequestMappingHandlerAdapter 在 getDefaultArgumentResolvers() 中按四大類別裝配 24+ 內建解析器:
@RequestParam、@PathVariable、@MatrixVariable、@ModelAttribute、@RequestBody、@RequestPart、@RequestHeader、@CookieValue、@ValueServletRequest、ServletResponse、HttpEntity、RedirectAttributes、Model、Map、Errors、SessionStatus、UriComponentsBuildergetCustomArgumentResolvers() 注入@RequestParam(required=true)、ServletModelAttributeMethodProcessor(綁定所有簡單類型)所有解析器被封裝進 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 方法用於註冊 PropertyEditor、Converter、Validator 等。其解析器初始化邏輯(getDefaultInitBinderArgumentResolvers())與一般引數解析器類似,但移除了 @ModelAttribute、@RequestBody、@RequestPart、@CookieValue、Map、Model、Errors、SessionStatus 等與資料綁定無關的解析器,僅保留 @RequestParam、@PathVariable、@MatrixVariable、@Value、ServletRequest/Response 及自定義解析器。這體現了「最小權限原則」:資料綁定階段只解析真正需要的參數類型。
HandlerMethodReturnValueHandler 介面同樣定義 supportsReturnType() 與 handleReturnValue()。getDefaultReturnValueHandlers() 裝配四類處理器:
ModelAndView、Model、View、HttpEntity、Callable、DeferredResult、AsyncTask@ModelAttribute、@ResponseBodyViewName(String)、MapModelAndViewResolver、最終兜底 ModelAttributeMethodProcessor(true)順序同樣關鍵:ModelAndView 最優先,String 視圖名與 Map 置於中段,兜底處理器最後。異步返回類型(Callable 等)專門處理非阻塞流程。
@ControllerAdvice 解決跨控制器共享 @ModelAttribute、@InitBinder、@ExceptionHandler 的需求,避免單繼承限制。初始化流程(initControllerAdviceCache()):
applicationContext.getBeanDefinitionNames() 獲取名稱,findAnnotationOnBean(name, ControllerAdvice.class) 過濾帶註解的 Bean。ControllerAdviceBean,按 @Order 或 Ordered 介面排序。HandlerMethodSelector.selectMethods() 提取該 Bean 中標註 @ModelAttribute 與 @InitBinder 的方法。modelAttributeAdviceCache、initBinderAdviceCache,Key 為 ControllerAdviceBean,Value 為方法集合。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 模式聚合多種策略,責任鏈動態分派@ControllerAdvice 掃描緩存機制,支援跨控制器關注點分離深入理解這套初始化機制,不僅能排查參數綁定、返回值處理、全域增強失效等疑難雜症,更能在需要自定義引數解析、返回值封裝、跨切面邏輯時,精準切入擴展點,寫出符合 Spring 精神的優雅程式碼。