Spring MVC學習教程之RequestMappingHandlerAdapter詳解

這篇文章主要給大家介紹了關於Spring MVC學習教程之RequestMappingHandlerAdapter的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑑,下面隨着小編來一起學習學習吧

前言

RequestMappingHandlerAdapter實現了HandlerAdapter接口,顧名思義,表示handler的adapter,這裏的handler指的是Spring處理具體請求的某個Controller的方法,也就是說HandlerAdapter指的是將當前請求適配到某個Handler的處理器。RequestMappingHandlerAdapter是HandlerAdapter的一個具體實現,主要用於將某個請求適配給@RequestMapping類型的Handler處理。

如下是HandlerMapping接口的聲明:

public interface HandlerAdapter {
// 用於判斷當前HandlerAdapter是否能夠處理當前請求
boolean supports(Object handler);

// 如果當前HandlerAdapter能夠用於適配當前請求,那麼就會處理當前請求中
// 諸如參數和返回值等信息,以便能夠直接委託給具體的Handler處理
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, 
Object handler) throws Exception;

// 獲取當前請求的最後更改時間,主要用於供給瀏覽器判斷當前請求是否修改過,
// 從而判斷是否可以直接使用之前緩存的結果
long getLastModified(HttpServletRequest request, Object handler);
}

1. supports()

HandlerAdapter.supports()方法的主要作用在於判斷當前的HandlerAdapter是否能夠支持當前的handler的適配。這裏的handler指的是某個Controller的方法,其是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法獲取到的。從這裏可以看出,HandlerMapping的作用主要是根據request請求獲取能夠處理當前request的handler,而HandlerAdapter的作用在於將request中的各個屬性,如request param適配爲handler能夠處理的形式。

關於HandlerAdapter.supports()方法,有這個方法的主要原因是,HandlerMapping是可以有多種實現的,Spring會遍歷這些具體的實現類,判斷哪一個能夠根據當前request產生一個handler,因而對於HandlerAdapter而言,其是不知道當前獲取到的handler具體是什麼形式的,不同的HandlerMapping產生的handler形式是不一樣的,比如RequestMappingHandlerMapping產生的handler則是封裝在HandlerMethod對象中的,因而這裏HandlerAdapter需要一個方法能夠快速過濾掉當前產生的handler是否爲其能夠進行適配的,這個方法就是HandlerAdapter.supports()方法。如下是該方法的實現:

// AbstractHandlerMethodAdapter
@Override
public final boolean supports(Object handler) {
// 判斷當前handler是否爲HandlerMethod類型,並且判斷supportsInternal()方法返回值是否爲true,
// 這裏supportsInternal()方法是提供給子類實現的一個方法,對於RequestMappingHandlerAdapter
// 而言,其返回值始終是true,因爲其只需要處理的handler是HandlerMethod類型的即可
return (handler instanceof HandlerMethod 
&& supportsInternal((HandlerMethod) handler));
}
// RequestMappingHandlerAdapter
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
// 這裏RequestMappingHandlerAdapter只是對supportsInternal()返回true,因爲其只需要
// 處理的handler類型是HandlerMethod類型即可
return true;
}

2. handle()

在supports()方法判斷了所處理的handler是HandlerMethod類型之後,RequestMappingHandlerAdapter就會調用handle()方法處理當前請求。該方法的主要作用在於有五點:

  • 獲取當前Spring容器中在方法上配置的標註了@ModelAttribute但是沒標註@RequestMapping註解的方法,在真正調用具體的handler之前會將這些方法依次進行調用;
  • 獲取當前Spring容器中標註了@InitBinder註解的方法,調用這些方法以對一些用戶自定義的參數進行轉換並且綁定;
  • 根據當前handler的方法參數標註的註解類型,如@RequestParam,@ModelAttribute等,獲取其對應的ArgumentResolver,以將request中的參數轉換爲當前方法中對應註解的類型;
  • 配合轉換而來的參數,通過反射調用具體的handler方法;
  • 通過ReturnValueHandler對返回值進行適配,比如ModelAndView類型的返回值就由ModelAndViewMethodReturnValueHandler處理,最終將所有的處理結果都統一封裝爲一個ModelAndView類型的返回值,這也是RequestMappingHandlerAdapter.handle()方法的返回值類型。

這裏我們首先看看RequestMappingHandlerAdapter.handle()方法的實現源碼:

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ModelAndView mav;
checkRequest(request);

// 判斷當前是否需要支持在同一個session中只能線性地處理請求
if (this.synchronizeOnSession) {
// 獲取當前請求的session對象
HttpSession session = request.getSession(false);
if (session != null) {
// 爲當前session生成一個唯一的可以用於鎖定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 對HandlerMethod進行參數等的適配處理,並調用目標handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 如果當前不存在session,則直接對HandlerMethod進行適配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// 如果當前不需要對session進行同步處理,則直接對HandlerMethod進行適配
mav = invokeHandlerMethod(request, response, handlerMethod);
}

// 判斷當前請求頭中是否包含Cache-Control請求頭,如果不包含,則對當前response進行處理,
// 爲其設置過期時間
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果當前SessionAttribute中存在配置的attributes,則爲其設置過期時間。
// 這裏SessionAttribute主要是通過@SessionAttribute註解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
// 如果當前不存在SessionAttributes,則判斷當前是否存在Cache-Control設置,
// 如果存在,則按照該設置進行response處理,如果不存在,則設置response中的
// Cache的過期時間爲-1,即立即失效
prepareResponse(response);
}
}

return mav;
}

上述代碼主要做了兩部分處理:①判斷當前是否對session進行同步處理,如果需要,則對其調用進行加鎖,不需要則直接調用;②判斷請求頭中是否包含Cache-Control請求頭,如果不包含,則設置其Cache立即失效。可以看到,對於HandlerMethod的具體處理是在invokeHandlerMethod()方法中進行的,如下是該方法的具體實現:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 獲取容器中全局配置的InitBinder和當前HandlerMethod所對應的Controller中
// 配置的InitBinder,用於進行參數的綁定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 獲取容器中全局配置的ModelAttribute和當前當前HandlerMethod所對應的Controller
// 中配置的ModelAttribute,這些配置的方法將會在目標方法調用之前進行調用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

// 將handlerMethod封裝爲一個ServletInvocableHandlerMethod對象,
// 該對象用於對當前request的整體調用流程進行了封裝
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 設置當前容器中配置的所有ArgumentResolver
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 設置當前容器中配置的所有ReturnValueHandler
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 將前面創建的WebDataBinderFactory設置到ServletInvocableHandlerMethod中
invocableMethod.setDataBinderFactory(binderFactory);
// 設置ParameterNameDiscoverer,該對象將按照一定的規則獲取當前參數的名稱
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 這裏initModel()方法主要作用是調用前面獲取到的@ModelAttribute標註的方法,
// 從而達到@ModelAttribute標註的方法能夠在目標Handler調用之前調用的目的
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

// 獲取當前的AsyncWebRequest,這裏AsyncWebRequest的主要作用是用於判斷目標
// handler的返回值是否爲WebAsyncTask或DefferredResult,如果是這兩種中的一種,
// 則說明當前請求的處理應該是異步的。所謂的異步,指的是當前請求會將Controller中
// 封裝的業務邏輯放到一個線程池中進行調用,待該調用有返回結果之後再返回到response中。
// 這種處理的優點在於用於請求分發的線程能夠解放出來,從而處理更多的請求,只有待目標任務
// 完成之後纔會回來將該異步任務的結果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils
.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

// 封裝異步任務的線程池,request和interceptors到WebAsyncManager中
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

// 這裏就是用於判斷當前請求是否有異步任務結果的,如果存在,則對異步任務結果進行封裝
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) 
asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
// 封裝異步任務的處理結果,雖然封裝的是一個HandlerMethod,但只是Spring簡單的封裝
// 的一個Callable對象,該對象中直接將調用結果返回了。這樣封裝的目的在於能夠統一的
// 進行右面的ServletInvocableHandlerMethod.invokeAndHandle()方法的調用
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}

// 對請求參數進行處理,調用目標HandlerMethod,並且將返回值封裝爲一個ModelAndView對象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

// 對封裝的ModelAndView進行處理,主要是判斷當前請求是否進行了重定向,如果進行了重定向,
// 還會判斷是否需要將FlashAttributes封裝到新的請求中
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
// 調用request destruction callbacks和對SessionAttributes進行處理
webRequest.requestCompleted();
}
}

上述代碼是RequestMappingHandlerAdapter處理請求的主要流程,其主要包含四個部分:①獲取當前容器中使用@InitBinder註解註冊的屬性轉換器;②獲取當前容器中使用@ModelAttribute標註但沒有使用@RequestMapping標註的方法,並且在調用目標方法之前調用這些方法;③判斷目標handler返回值是否使用了WebAsyncTask或DefferredResult封裝,如果封裝了,則按照異步任務的方式進行執行;④處理請求參數,調用目標方法和處理返回值。這裏我們首先看RequestMappingHandlerAdapter是如何處理標註@InitBinder的方法的,如下是getDataBinderFactory()方法的源碼:

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) 
throws Exception {
// 判斷當前緩存中是否緩存了當前bean所需要裝配的InitBinder方法,如果存在,則直接從緩存中取,
// 如果不存在,則在當前bean中進行掃描獲取
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
// 在當前bean中查找所有標註了@InitBinder註解的方法,這裏INIT_BINDER_METHODS就是一個
// 選擇器,表示只獲取使用@InitBinder標註的方法
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}

// 這裏initBinderAdviceCache是在RequestMappingHandlerAdapter初始化時同步初始化的,
// 其內包含的方法有如下兩個特點:①當前方法所在類使用@ControllerAdvice進行標註了;
// ②當前方法使用@InitBinder進行了標註。也就是說其內保存的方法可以理解爲是全局類型
// 的參數綁定方法
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
// 這裏判斷的是當前配置的全局類型的InitBinder是否能夠應用於當前bean,
// 判斷的方式主要在@ControllerAdvice註解中進行了聲明,包括通過包名,類所在的包,
// 接口或者註解的形式限定的範圍
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});

// 這裏是將當前HandlerMethod所在bean中的InitBinder添加到需要執行的initBinderMethods中。
// 這裏從添加的順序可以看出,全局類型的InitBinder會在當前bean中的InitBinder之前執行
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}

// 將需要執行的InitBinder封裝到InitBinderDataBinderFactory中
return createDataBinderFactory(initBinderMethods);
}

這裏獲取InitBinder的方式主要有兩種,一種是獲取全局配置的InitBinder,全局類型的InitBinder需要聲明的類上使用@ControllerAdvice進行標註,並且聲明方法上使用@InitBinder進行標註;另一種則是獲取當前handler所在類中的使用@InitBinder註解標註的方法。這兩種InitBinder都會執行,只不過全局類型的InitBinder會先於局部類型的InitBinder執行。關於使用@InitBinder標註的方法的執行時間點,需要說明的是,因爲其與參數綁定有關,因而其只會在參數綁定時纔會執行。

這裏我們繼續看RequestMappingHandlerAdapter是如何獲取@ModelAttribute標註的方法並且執行的,如下是getModelFactory()方法的源碼:

private ModelFactory getModelFactory(HandlerMethod handlerMethod, 
WebDataBinderFactory binderFactory) {
// 這裏SessionAttributeHandler的作用是聲明幾個屬性,使其能夠在多個請求之間共享,
// 並且其能夠保證當前request返回的model中始終保有這些屬性
SessionAttributesHandler sessionAttrHandler = 
getSessionAttributesHandler(handlerMethod);

// 判斷緩存中是否保存有當前handler執行之前所需要執行的標註了@ModelAttribute的方法
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
// 如果緩存中沒有相關屬性,那麼就在當前bean中查找所有使用@ModelAttribute標註,但是
// 沒有使用@RequestMapping標註的方法,並將這些方法緩存起來
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}

// 獲取全局的使用@ModelAttribute標註,但是沒有使用@RequestMapping標註的方法,
// 這裏全局類型的方法的聲明方式需要注意的是,其所在的bean必須使用@ControllerAdvice進行標註
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
// 判斷@ControllerAdvice中指定的作用的bean範圍與當前bean是否匹配,匹配了纔會對其應用
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});

// 將當前方法中使用@ModelAttribute標註的方法添加到需要執行的attrMethods中。從這裏的添加順序
// 可以看出,全局類型的方法將會先於局部類型的方法執行
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}

// 將需要執行的方法等數據封裝爲ModelFactory對象
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

上述getModelFactory()方法主要工作還是獲取當前需要先於目標handler執行的方法,並且獲取的方式與前面的InitBinder非常的相似,這裏就不再贅述。關於這裏獲取的方法,其具體的執行過程實際上是在後面的ModelFactory.initModel()方法中進行。這裏我們直接閱讀該方法的源碼:

public void initModel(NativeWebRequest request, ModelAndViewContainer container,
HandlerMethod handlerMethod) throws Exception {

// 在當前request中獲取使用@SessionAttribute註解聲明的參數
Map<String, ?> sessionAttributes = 
this.sessionAttributesHandler.retrieveAttributes(request);
// 將@SessionAttribute聲明的參數封裝到ModelAndViewContainer中
container.mergeAttributes(sessionAttributes);
// 調用前面獲取的使用@ModelAttribute標註的方法
invokeModelAttributeMethods(request, container);

// 這裏首先獲取目標handler執行所需的參數中與@SessionAttribute同名或同類型的參數,
// 也就是handler想要直接從@SessionAttribute中聲明的參數中獲取的參數。然後對這些參數
// 進行遍歷,首先判斷request中是否包含該屬性,如果不包含,則從之前的SessionAttribute緩存
// 中獲取,如果兩個都沒有,則直接拋出異常
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" 
+ name + "'", name);
}
container.addAttribute(name, value);
}
}
}

這裏initModel()方法主要做了兩件事:①保證@SessionAttribute聲明的參數的存在;②調用使用@ModelAttribute標註的方法。我們直接閱讀invokeModelAttributeMethods()方法的源碼:

private void invokeModelAttributeMethods(NativeWebRequest request, 
ModelAndViewContainer container) throws Exception {

while (!this.modelMethods.isEmpty()) {
// 這裏getNextModelMethod()方法始終會獲取modelMethods中的第0號爲的方法,
// 後續該方法執行完了之後則會將該方法從modelMethods移除掉,因而這裏while
// 循環只需要判斷modelMethods是否爲空即可
InvocableHandlerMethod modelMethod = 
getNextModelMethod(container).getHandlerMethod();
// 獲取當前方法中標註的ModelAttribute屬性,然後判斷當前request中是否有與該屬性中name字段
// 標註的值相同的屬性,如果存在,並且當前ModelAttribute設置了不對該屬性進行綁定,那麼
// 就直接略過當前方法的執行
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}

// 通過ArgumentResolver對方法參數進行處理,並且調用目標方法
Object returnValue = modelMethod.invokeForRequest(request, container);

// 如果當前方法的返回值不爲空,則判斷當前@ModelAttribute是否設置了需要綁定返回值,
// 如果設置了,則將返回值綁定到請求中,後續handler可以直接使用該參數
if (!modelMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, 
modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}

// 如果request中不包含該參數,則將該返回值添加到ModelAndViewContainer中,
// 供handler使用
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}

這裏調用使用@ModelAttribute標註的方法的方式比較簡單,主要需要注意的是,對於調用結果,如果當前request中沒有同名的參數,則會將調用結果添加到ModelAndViewContainer中,以供給後續handler使用。

在調用完使用上述方法之後,Spring會判斷當前handler的返回值是否爲WebAsyncTask或DefferredResult類型,如果是這兩種類型的一種,那麼就會將這些任務放入一個線程池中進行異步調用,而當前線程則可以繼續進行請求的分發。這裏這種設計的目的是,默認情況下Spring處理請求都是同步的,也就是說進行請求分發的線程是會調用用戶所聲明的handler方法的,那麼如果用戶聲明的handler執行時間較長,就可能導致Spring用於請求處理的線程都耗在了處理這些業務代碼上,也就導致後續的請求必須等待,這在高併發的場景中是不能被允許的,因而這裏Spring提供了一種異步任務處理的方式,也就是進行請求分發的線程只需要將用戶的業務任務放到線程池中執行即可,其自身可以繼續進行其他的請求的分發。如果線程池中的任務處理完成,其會通知Spring將處理結果返回給調用方。關於異步任務的處理流程,我們後面會使用專門的章節進行講解,這裏只是簡單的講解其主要功能。

在進行了相關前置方法調用和異步任務的判斷之後,RequestMappingHandlerAdapter就會開始調用目標handler了。調用過程在ServletInvocableHandlerMethod.invokeAndHandle()方法中,如下是該方法的源碼:

public void invokeAndHandle(ServletWebRequest webRequest, 
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

// 對目標handler的參數進行處理,並且調用目標handler
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 設置相關的返回狀態
setResponseStatus(webRequest);

// 如果請求處理完成,則設置requestHandled屬性
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null 
|| mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
// 如果請求失敗,但是有錯誤原因,那麼也會設置requestHandled屬性
mavContainer.setRequestHandled(true);
return;
}

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 遍歷當前容器中所有ReturnValueHandler,判斷哪種handler支持當前返回值的處理,
// 如果支持,則使用該handler處理該返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", 
returnValue), ex);
}
throw ex;
}
}

對於handler的調用過程,這裏主要分爲三個步驟:①處理請求參數進行處理,將request中的參數封裝爲當前handler的參數的形式;②通過反射調用當前handler;③對方法的返回值進行處理,以將其封裝爲一個ModleAndView對象。這裏第一步和第二步封裝在了invokeForRequest()方法中,我們首先看該方法的源碼:

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

// 將request中的參數轉換爲當前handler的參數形式
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), 
getBeanType()) + "' with arguments " + Arrays.toString(args));
}
// 這裏doInvoke()方法主要是結合處理後的參數,使用反射對目標方法進行調用
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), 
getBeanType()) + "] returned [" + returnValue + "]");
}
return returnValue;
}

// 本方法主要是通過當前容器中配置的ArgumentResolver對request中的參數進行轉化,
// 將其處理爲目標handler的參數的形式
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable 
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

// 獲取當前handler所聲明的所有參數,主要包括參數名,參數類型,參數位置,所標註的註解等等屬性
MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// providedArgs是調用方提供的參數,這裏主要是判斷這些參數中是否有當前類型
// 或其子類型的參數,如果有,則直接使用調用方提供的參數,對於請求處理而言,默認情況下,
// 調用方提供的參數都是長度爲0的數組
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}

// 如果在調用方提供的參數中不能找到當前類型的參數值,則遍歷Spring容器中所有的
// ArgumentResolver,判斷哪種類型的Resolver支持對當前參數的解析,這裏的判斷
// 方式比較簡單,比如RequestParamMethodArgumentResolver就是判斷當前參數
// 是否使用@RequestParam註解進行了標註
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
// 如果能夠找到對當前參數進行處理的ArgumentResolver,則調用其
// resolveArgument()方法從request中獲取對應的參數值,並且進行轉換
args[i] = this.argumentResolvers.resolveArgument(
parameter, mavContainer, request, this.dataBinderFactory);
continue;
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", 
i), ex);
}
throw ex;
}
}

// 如果進行了參數處理之後當前參數還是爲空,則拋出異常
if (args[i] == null) {
throw new IllegalStateException("Could not resolve method parameter at index " 
+ parameter.getParameterIndex() + " in " 
+ parameter.getExecutable().toGenericString() 
+ ": " + getArgumentResolutionErrorMessage("No suitable resolver for",i));
}
}
return args;
}

關於handler的調用,可以看到,這裏的實現也是比較簡單的,首先是遍歷所有的參數,並且查找哪種ArgumentResolver能夠處理當前參數,找到了則按照具體的Resolver定義的方式進行處理即可。在所有的參數處理完成之後,RequestMappingHandlerAdapter就會使用反射調用目標handler。

對於返回值的處理,其形式與對參數的處理非常相似,都是對ReturnValueHandler進行遍歷,判斷哪種Handler能夠支持當前返回值的處理,如果找到了,則按照其規則進行處理即可。如下是該過程的主要流程代碼:

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

// 獲取能夠處理當前返回值的Handler,比如如果返回值是ModelAndView類型,那麼這裏的handler就是
// ModelAndViewMethodReturnValueHandler
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " 
+ returnType.getParameterType().getName());
}

// 通過獲取到的handler處理返回值,並將其封裝到ModelAndViewContainer中
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

// 本方法的主要作用是獲取能夠處理當前返回值的ReturnValueHandler
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, 
MethodParameter returnType) {
// 判斷返回值是否爲異步類型的返回值,即WebAsyncTask或DefferredResult
boolean isAsyncValue = isAsyncReturnValue(value, returnType);

// 對所有的ReturnValueHandler進行遍歷,判斷其是否支持當前返回值的處理。這裏如果當前返回值
// 是異步類型的返回值,還會判斷當前ReturnValueHandler是否爲
// AsyncHandlerMethodReturnValueHandler類型,如果不是,則會繼續查找
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}

// 判斷是否支持返回值處理的主要位置,比如ModelAndViewMethodReturnValueHandler就會
// 判斷返回值是否爲ModelAndView類型,如果是,則表示其是當前ReturnValuleHandler所支持的類型
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}

3. 小結

本文首先講解了RequestMappingHandlerMapping所做的工作與RequestMappingHandlerAdapter的區別,然後講解RequestMappingHandlerAdapter是如何判斷當前的handler是否爲其所支持的類型的,最後詳細講解了其是如果將request適配爲目標handler能夠調用的形式的。總的來講,RequestMappingHandlerAdapter的主要作用就是調用RequestMappingHandlerMapping所獲取到的handler,然後將返回值封裝爲一個ModelAndView對象,該對象中保存了所要渲染的視圖名稱和渲染視圖時所需要的參數值,而具體的渲染過程則是通過View對象進行的。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對神馬文庫的支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章