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

SpringMVC 源碼深度解析:HandlerMethod 家族架構與請求處理核心流程

     編輯:Java 綜合教學

HandlerMethod 類族架構概覽

SpringMVC 通過三層繼承體系封裝控制器方法調用全流程:

  • HandlerMethod:基礎封裝,持有 Bean、Method、參數元數據、BeanFactory 引用。
  • InvocableHandlerMethod:增加參數解析與方法調用能力,委派 HandlerMethodArgumentResolver。
  • ServletInvocableHandlerMethod:增加返回值處理、@ResponseStatus 支持,適配 Servlet 環境。

HandlerMethod 類圖

核心類源碼逐層剖析

1. HandlerMethod:方法元數據容器

public class HandlerMethod {
    protected final Log logger = LogFactory.getLog(HandlerMethod.class);
    private final Object bean;                 // Controller 實例或 Bean 名稱
    private final Method method;               // 處理方法
    private final BeanFactory beanFactory;     // 容器引用,用於解析 Bean 名稱
    private final MethodParameter[] parameters; // 方法參數元數據(含返回值 index=-1)
    private final Method bridgedMethod;        // 橋接方法(泛型擦除產生)

    // 關鍵方法:若 bean 為 String(Bean 名),從容器獲取實例返回新 HandlerMethod
    public HandlerMethod createWithResolvedBean() {
        Object handler = this.bean;
        if (this.bean instanceof String) {
            handler = this.beanFactory.getBean((String) this.bean);
        }
        return new HandlerMethod(this, handler);
    }
}

MethodParameter 封裝單個參數完整信息:Method/Constructor、參數索引、類型、泛型、注解、參數名(需 ParameterNameDiscoverer 支持)、嵌套層級等。

2. InvocableHandlerMethod:參數解析與調用引擎

public class InvocableHandlerMethod extends HandlerMethod {
    private HandlerMethodArgumentResolverComposite argumentResolvers;
    private WebDataBinderFactory dataBinderFactory;
    private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    // 核心:構建方法調用實參數組
    private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) {
        MethodParameter[] params = getMethodParameters();
        Object[] args = new Object[params.length];
        for (int i = 0; i < params.length; i++) {
            MethodParameter param = params[i];
            param.initParameterNameDiscovery(parameterNameDiscoverer);
            GenericTypeResolver.resolveParameterType(param, getBean().getClass());
            
            // 1. 優先從 providedArgs(如 @ModelAttribute 預置對象)按類型匹配
            args[i] = resolveProvidedArgument(param, providedArgs);
            if (args[i] != null) continue;
            
            // 2. 委派參數解析器解析(@RequestParam, @PathVariable, @RequestBody 等)
            if (argumentResolvers.supportsParameter(param)) {
                args[i] = argumentResolvers.resolveArgument(param, mavContainer, request, dataBinderFactory);
                continue;
            }
            // 3. 無解析器支持則拋異常
            throw new IllegalStateException("No suitable resolver for argument " + i);
        }
        return args;
    }

    // 實際調用:設置可訪問性、異常解包
    private Object invoke(Object... args) throws Exception {
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            return getBridgedMethod().invoke(getBean(), args);
        } catch (InvocationTargetException e) {
            Throwable target = e.getTargetException();
            if (target instanceof RuntimeException) throw (RuntimeException) target;
            if (target instanceof Error) throw (Error) target;
            if (target instanceof Exception) throw (Exception) target;
            throw new IllegalStateException("Invocation failed", target);
        }
    }
}

3. ServletInvocableHandlerMethod:Web 層適配與返回值處理

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    private ResponseStatus responseStatus; // @ResponseStatus 註解信息
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;

    // 入口:DispatcherServlet 處理器調用鏈最終調用此方法
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) {
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 父類調用
        
        setResponseStatus(webRequest); // 處理 @ResponseStatus
        
        // 判斷請求是否已完成(無返回值 + 無狀態碼 + 未標記 handled)
        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(responseReason)) { // 有錯誤原因視為已處理
            mavContainer.setRequestHandled(true);
            return;
        }
        
        mavContainer.setRequestHandled(false);
        // 委派返回值處理器(View, @ResponseBody, HttpEntity, Model 等)
        returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    }

    private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
        if (responseStatus == null) return;
        if (StringUtils.hasText(responseReason)) {
            webRequest.getResponse().sendError(responseStatus.value(), responseReason);
        } else {
            webRequest.getResponse().setStatus(responseStatus.value());
        }
        // 供 RedirectView 檢測
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
    }
}

核心設計模式與擴展點

組件職責擴展接口典型實現
參數解析將請求數據轉換為方法參數HandlerMethodArgumentResolverRequestParamMethodArgumentResolver, RequestResponseBodyMethodProcessor
返回值處理將方法返回值轉換為響應HandlerMethodReturnValueHandlerViewMethodReturnValueHandler, RequestResponseBodyMethodProcessor
數據綁定表單數據綁定到對象、校驗WebDataBinder + ValidatorConfigurableWebBindingInitializer

源碼閱讀建議

  1. 從 DispatcherServlet.doDispatch() 入手,追蹤 HandlerAdapter.handle() → ServletInvocableHandlerMethod.invokeAndHandle()。
  2. 重點理解 HandlerMethodArgumentResolverComposite 的順序優先級機制(Ordered 接口)。
  3. 關注 ModelAndViewContainer 作為請求處理上下文的狀態流轉(requestHandled、redirectModel 等)。
  4. 結合斷點調試觀察 providedArgs 在 @ModelAttribute、@SessionAttributes 等場景下的注入過程。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved