該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 Spring MVC 原始碼分析 GitHub 地址 進行閱讀
Spring 版本:5.2.4.RELEASE
該系列其他文件請檢視:《精盡 Spring MVC 原始碼分析 - 文章導讀》
HandlerAdapter 元件
HandlerAdapter 元件,處理器的介面卡。因為處理器 handler
的型別是 Object 型別,需要有一個呼叫者來實現 handler
是怎麼被執行。Spring 中的處理器的實現多變,比如使用者的處理器可以實現 Controller 介面或者 HttpRequestHandler 介面,也可以用 @RequestMapping
註解將方法作為一個處理器等,這就導致 Spring MVC 無法直接執行這個處理器。所以這裡需要一個處理器介面卡,由它去執行處理器
由於 HandlerMapping 元件涉及到的內容較多,考慮到內容的排版,所以將這部分內容拆分成了五個模組,依次進行分析:
- 《HandlerAdapter 元件(一)之 HandlerAdapter》
- 《HandlerAdapter 元件(二)之 ServletInvocableHandlerMethod》
- 《HandlerAdapter 元件(三)之 HandlerMethodArgumentResolver》
- 《HandlerAdapter 元件(四)之 HandlerMethodReturnValueHandler》
- 《HandlerAdapter 元件(五)之 HttpMessageConverter》
HandlerAdapter 元件(二)之 ServletInvocableHandlerMethod
本文是接著《HandlerAdapter 元件(一)之 HandlerAdapter》一文來分享 ServletInvocableHandlerMethod 元件。在 HandlerAdapter
執行處理器的過程中,主要的任務還是交由它來完成的。ServletInvocableHandlerMethod 封裝 HandlerMethod
處理器物件,它還包含 HandlerMethodArgumentResolver
引數解析器和 HandlerMethodReturnValueHandler
返回值處理器等元件。雖然內容不多,但還是有必要另一篇進行分析。
回顧
先來回顧一下 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 物件,然後設定引數解析器和返回值處理器 -
這裡設定了 ServletInvocableHandlerMethod 物件的
resolvers
、parameterNameDiscoverer
和returnValueHandlers
相關屬性 -
呼叫
invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs)
方法,執行處理器
類圖
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);
}
-
呼叫
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; }
- 先從
providedArgs
中獲得引數。如果獲得到,則進入下一個引數的解析。預設情況下,providedArgs
引數不會傳遞,所以可以暫時先忽略。保證核心邏輯的理解 - 判斷
argumentResolvers
是否支援當前的引數解析。如果支援,則進行解析。關於 HandlerMethodArgumentResolverComposite 的詳細解析,見《HandlerAdapter 元件(三)之 HandlerMethodArgumentResolver》
- 先從
-
呼叫
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); } } }
- 設定方法為可訪問
- 通過反射執行該方法
注意,這裡獲取到的 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;
}
}
-
呼叫 InvocableHandlerMethod 父類的
invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs)
方法,執行呼叫 -
呼叫
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); }
-
設定 ModelAndViewContainer 為請求已處理,返回,和 @ResponseStatus 註解相關
-
設定 ModelAndViewContainer 為請求未處理
-
呼叫
HandlerMethodReturnValueHandlerComposite
的handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
方法,處理返回值。詳細解析,見《HandlerAdapter 元件(四)之 HandlerMethodReturnValueHandler》
總結
在 HandlerAdapter
執行 HandlerMethod
處理器的過程中,會將該處理器封裝成 ServletInvocableHandlerMethod 物件,通過該物件來執行處理器。因為 HandlerMethod
處理器裡面僅包含了方法的所有資訊,如何解析引數、呼叫對應的方法、以及處理返回結果,它本身並不知道如何去處理,這裡 Spring MVC 藉助於 ServletInvocableHandlerMethod 物件來進行操作,原理就是通過反射機制執行該方法
其中涉及到的 HandlerMethodArgumentResolver
引數解析器和 HandlerMethodReturnValueHandler
返回值處理器是比較關鍵的兩個元件,在接下來會逐個進行分析
參考文章:芋道原始碼《精盡 Spring MVC 原始碼分析》