精盡Spring MVC源碼分析 - HandlerAdapter 組件(二)之 ServletInvocableHandlerMethod

該系列文檔是本人在學習 Spring MVC 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋 Spring MVC 源碼分析 GitHub 地址 進行閱讀html

Spring 版本:5.2.4.RELEASEjava

該系列其餘文檔請查看:《精盡 Spring MVC 源碼分析 - 文章導讀》git

HandlerAdapter 組件

HandlerAdapter 組件,處理器的適配器。由於處理器 handler 的類型是 Object 類型,須要有一個調用者來實現 handler 是怎麼被執行。Spring 中的處理器的實現多變,好比用戶的處理器能夠實現 Controller 接口或者 HttpRequestHandler 接口,也能夠用 @RequestMapping 註解將方法做爲一個處理器等,這就致使 Spring MVC 沒法直接執行這個處理器。因此這裏須要一個處理器適配器,由它去執行處理器github

因爲 HandlerMapping 組件涉及到的內容較多,考慮到內容的排版,因此將這部份內容拆分紅了五個模塊,依次進行分析:web

HandlerAdapter 組件(二)之 ServletInvocableHandlerMethod

本文是接着《HandlerAdapter 組件(一)之 HandlerAdapter》一文來分享 ServletInvocableHandlerMethod 組件。在 HandlerAdapter 執行處理器的過程當中,主要的任務仍是交由它來完成的。ServletInvocableHandlerMethod 封裝 HandlerMethod 處理器對象,它還包含 HandlerMethodArgumentResolver 參數解析器和 HandlerMethodReturnValueHandler 返回值處理器等組件。雖然內容很少,但仍是有必要另外一篇進行分析。spring

回顧

先來回顧一下 RequestMappingHandlerAdapter 是如何建立 ServletInvocableHandlerMethod 對象的,能夠回到 《HandlerAdapter 組件(一)之 HandlerAdapter》RequestMappingHandlerAdapter 小節下面的 invokeHandlerMethod 方法,以下:數組

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, 
                                           HandlerMethod handlerMethod) throws Exception {
    // ... 省略相關代碼
    // <4> 建立 ServletInvocableHandlerMethod 對象,並設置其相關屬性
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if (this.argumentResolvers != null) {
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
	// ... 省略相關代碼
    // <9> 執行調用
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    // ... 省略相關代碼
}

protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
    return new ServletInvocableHandlerMethod(handlerMethod);
}
  • HandlerMethod 處理器封裝成 ServletInvocableHandlerMethod 對象,而後設置參數解析器和返回值處理器mvc

  • 這裏設置了 ServletInvocableHandlerMethod 對象的 resolversparameterNameDiscovererreturnValueHandlers 相關屬性app

  • 調用invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs)方法,執行處理器ide

類圖

ServletInvocableHandlerMethod 的總體類圖以下:

依次分析

HandlerMethod

org.springframework.web.method.HandlerMethod,處理器的方法的封裝對象

《HandlerMapping 組件(三)之 AbstractHandlerMethodMapping》AbstractHandlerMethodMapping 小節中已經分析過該對象

InvocableHandlerMethod

org.springframework.web.method.support.InvocableHandlerMethod,繼承 HandlerMethod 類,可 invoke 調用的 HandlerMethod 實現類。

😈 也就是說,HandlerMethod 只提供了處理器的方法的基本信息,不提供調用邏輯。

構造方法

public class InvocableHandlerMethod extends HandlerMethod {
    /** 無參時的入參,空數組 */
	private static final Object[] EMPTY_ARGS = new Object[0];
    /** 數據綁定器工廠 */
	@Nullable
	private WebDataBinderFactory dataBinderFactory;
    /** 參數解析器組合對象 */
	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
	/** 方法的參數名稱發現器 */
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
}

invokeForRequest

invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,執行請求,方法以下:

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
        Object... providedArgs) throws Exception {

    // <1> 解析參數
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    // <2> 執行調用
    return doInvoke(args);
}
  1. 調用 getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,解析方法的參數們,以下:

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
        // 得到方法的參數
        MethodParameter[] parameters = getMethodParameters();
        // 無參,返回空數組
        if (ObjectUtils.isEmpty(parameters)) {
            return EMPTY_ARGS;
        }
        // 將參數解析成對應的類型
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            // 得到當前遍歷的 MethodParameter 對象,並設置 parameterNameDiscoverer 到其中
            MethodParameter parameter = parameters[i];
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            // <1> 先從 providedArgs 中得到參數。若是得到到,則進入下一個參數的解析,默認狀況 providedArgs 不會傳參
            args[i] = findProvidedArgument(parameter, providedArgs);
            if (args[i] != null) {
                continue;
            }
             // <2> 判斷 resolvers 是否支持當前的參數解析
            if (!this.resolvers.supportsParameter(parameter)) {
                throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
            }
            try {
                // 執行解析,解析成功後,則進入下一個參數的解析
                args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
            }
            catch (Exception ex) {
                // Leave stack trace for later, exception may actually be resolved and handled...
                if (logger.isDebugEnabled()) {
                    String exMsg = ex.getMessage();
                    if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
                        logger.debug(formatArgumentError(parameter, exMsg));
                    }
                }
                throw ex;
            }
        }
        return args;
    }
    1. 先從 providedArgs 中得到參數。若是得到到,則進入下一個參數的解析。默認狀況下,providedArgs 參數不會傳遞,因此能夠暫時先忽略。保證核心邏輯的理解
    2. 判斷 argumentResolvers 是否支持當前的參數解析。若是支持,則進行解析。關於 HandlerMethodArgumentResolverComposite 的詳細解析,見《HandlerAdapter 組件(三)之 HandlerMethodArgumentResolver》
  2. 調用 doInvoke(Object... args) 方法,執行方法的調用,方法以下:

    @Nullable
    protected Object doInvoke(Object... args) throws Exception {
        // <1> 設置方法爲可訪問
        ReflectionUtils.makeAccessible(getBridgedMethod());
        try {
            // <2> 執行調用
            return getBridgedMethod().invoke(getBean(), args);
        }
        catch (IllegalArgumentException ex) {
            assertTargetBean(getBridgedMethod(), getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        }
        catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            }
            else if (targetException instanceof Error) {
                throw (Error) targetException;
            }
            else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            }
            else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }
    1. 設置方法爲可訪問
    2. 經過反射執行該方法

    注意,這裏獲取到的 Method 對象多是橋接方法,橋接方法:若是泛型對象,編譯器則會自動生成一個橋接方法(java1.5向後兼容)

ServletInvocableHandlerMethod

org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod,繼承 InvocableHandlerMethod 類,用於 DispatcherServlet 執行 HandlerMethod 處理器

構造方法

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
	private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
    
    /** 返回結果處理器組合對象 */
	@Nullable
	private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
    
    public ServletInvocableHandlerMethod(Object handler, Method method) {
		super(handler, method);
	}

	public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
		super(handlerMethod);
	}
}

invokeAndHandle

invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,處理請求,執行處理器,並處理返回結果,方法以下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // <1> 執行調用
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    // <2> 設置響應狀態碼
    setResponseStatus(webRequest);

    // <3> 設置 ModelAndViewContainer 爲請求已處理,返回,和 @ResponseStatus 註解相關
    if (returnValue == null) {
        if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
        }
    } else if (StringUtils.hasText(getResponseStatusReason())) {
        mavContainer.setRequestHandled(true);
        return;
    }

    // <4> 設置 ModelAndViewContainer 爲請求未處理
    mavContainer.setRequestHandled(false);
    Assert.state(this.returnValueHandlers != null, "No return value handlers");
    try {
        // <5> 處理返回值
        this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    } catch (Exception ex) {
        if (logger.isTraceEnabled()) {
            logger.trace(formatErrorForReturnValue(returnValue), ex);
        }
        throw ex;
    }
}
  1. 調用 InvocableHandlerMethod 父類的 invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) 方法,執行調用

  2. 調用 setResponseStatus(ServletWebRequest webRequest) 設置響應的狀態碼,方法以下:

    private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
        // 得到狀態碼,和 @ResponseStatus 註解相關
        HttpStatus status = getResponseStatus();
        if (status == null) {
            return;
        }
        // 設置響應的狀態碼
        HttpServletResponse response = webRequest.getResponse();
        if (response != null) {
            String reason = getResponseStatusReason();
            if (StringUtils.hasText(reason)) { // 有 reason ,則設置 status + reason
                response.sendError(status.value(), reason);
            } else { // 無 reason ,則僅設置 status
                response.setStatus(status.value());
            }
        }
    
        // To be picked up by RedirectView
        // 爲了 RedirectView ,因此進行設置
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
    }
  3. 設置 ModelAndViewContainer 爲請求已處理,返回,和 @ResponseStatus 註解相關

  4. 設置 ModelAndViewContainer 爲請求未處理

  5. 調用 HandlerMethodReturnValueHandlerCompositehandleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) 方法,處理返回值。詳細解析,見《HandlerAdapter 組件(四)之 HandlerMethodReturnValueHandler》

總結

HandlerAdapter 執行 HandlerMethod 處理器的過程當中,會將該處理器封裝成 ServletInvocableHandlerMethod 對象,經過該對象來執行處理器。由於 HandlerMethod 處理器裏面僅包含了方法的全部信息,如何解析參數、調用對應的方法、以及處理返回結果,它自己並不知道如何去處理,這裏 Spring MVC 藉助於 ServletInvocableHandlerMethod 對象來進行操做,原理就是經過反射機制執行該方法

其中涉及到的 HandlerMethodArgumentResolver 參數解析器和 HandlerMethodReturnValueHandler 返回值處理器是比較關鍵的兩個組件,在接下來會逐個進行分析

參考文章:芋道源碼《精盡 Spring MVC 源碼分析》

相關文章
相關標籤/搜索