溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring MVC之RequestMappingHandlerAdapter的示例分析

發(fā)布時間:2021-08-26 14:48:52 來源:億速云 閱讀:258 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關(guān)Spring MVC之RequestMappingHandlerAdapter的示例分析的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

前言

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

如下是HandlerMapping接口的聲明:

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

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

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

1. supports()

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

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

// AbstractHandlerMethodAdapter
@Override
public final boolean supports(Object handler) {
// 判斷當(dāng)前handler是否為HandlerMethod類型,并且判斷supportsInternal()方法返回值是否為true,
// 這里supportsInternal()方法是提供給子類實現(xiàn)的一個方法,對于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就會調(diào)用handle()方法處理當(dāng)前請求。該方法的主要作用在于有五點:

  • 獲取當(dāng)前Spring容器中在方法上配置的標(biāo)注了@ModelAttribute但是沒標(biāo)注@RequestMapping注解的方法,在真正調(diào)用具體的handler之前會將這些方法依次進行調(diào)用;

  • 獲取當(dāng)前Spring容器中標(biāo)注了@InitBinder注解的方法,調(diào)用這些方法以對一些用戶自定義的參數(shù)進行轉(zhuǎn)換并且綁定;

  • 根據(jù)當(dāng)前handler的方法參數(shù)標(biāo)注的注解類型,如@RequestParam,@ModelAttribute等,獲取其對應(yīng)的ArgumentResolver,以將request中的參數(shù)轉(zhuǎn)換為當(dāng)前方法中對應(yīng)注解的類型;

  • 配合轉(zhuǎn)換而來的參數(shù),通過反射調(diào)用具體的handler方法;

  • 通過ReturnValueHandler對返回值進行適配,比如ModelAndView類型的返回值就由ModelAndViewMethodReturnValueHandler處理,最終將所有的處理結(jié)果都統(tǒng)一封裝為一個ModelAndView類型的返回值,這也是RequestMappingHandlerAdapter.handle()方法的返回值類型。

這里我們首先看看RequestMappingHandlerAdapter.handle()方法的實現(xiàn)源碼:

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

ModelAndView mav;
checkRequest(request);

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

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

return mav;
}

上述代碼主要做了兩部分處理:①判斷當(dāng)前是否對session進行同步處理,如果需要,則對其調(diào)用進行加鎖,不需要則直接調(diào)用;②判斷請求頭中是否包含Cache-Control請求頭,如果不包含,則設(shè)置其Cache立即失效??梢钥吹剑瑢τ贖andlerMethod的具體處理是在invokeHandlerMethod()方法中進行的,如下是該方法的具體實現(xiàn):

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

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

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

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

// 獲取當(dāng)前的AsyncWebRequest,這里AsyncWebRequest的主要作用是用于判斷目標(biāo)
// handler的返回值是否為WebAsyncTask或DefferredResult,如果是這兩種中的一種,
// 則說明當(dāng)前請求的處理應(yīng)該是異步的。所謂的異步,指的是當(dāng)前請求會將Controller中
// 封裝的業(yè)務(wù)邏輯放到一個線程池中進行調(diào)用,待該調(diào)用有返回結(jié)果之后再返回到response中。
// 這種處理的優(yōu)點在于用于請求分發(fā)的線程能夠解放出來,從而處理更多的請求,只有待目標(biāo)任務(wù)
// 完成之后才會回來將該異步任務(wù)的結(jié)果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils
.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);

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

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

// 對請求參數(shù)進行處理,調(diào)用目標(biāo)HandlerMethod,并且將返回值封裝為一個ModelAndView對象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}

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

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

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

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

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

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

這里獲取InitBinder的方式主要有兩種,一種是獲取全局配置的InitBinder,全局類型的InitBinder需要聲明的類上使用@ControllerAdvice進行標(biāo)注,并且聲明方法上使用@InitBinder進行標(biāo)注;另一種則是獲取當(dāng)前handler所在類中的使用@InitBinder注解標(biāo)注的方法。這兩種InitBinder都會執(zhí)行,只不過全局類型的InitBinder會先于局部類型的InitBinder執(zhí)行。關(guān)于使用@InitBinder標(biāo)注的方法的執(zhí)行時間點,需要說明的是,因為其與參數(shù)綁定有關(guān),因而其只會在參數(shù)綁定時才會執(zhí)行。

這里我們繼續(xù)看RequestMappingHandlerAdapter是如何獲取@ModelAttribute標(biāo)注的方法并且執(zhí)行的,如下是getModelFactory()方法的源碼:

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

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

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

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

// 將需要執(zhí)行的方法等數(shù)據(jù)封裝為ModelFactory對象
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}

上述getModelFactory()方法主要工作還是獲取當(dāng)前需要先于目標(biāo)handler執(zhí)行的方法,并且獲取的方式與前面的InitBinder非常的相似,這里就不再贅述。關(guān)于這里獲取的方法,其具體的執(zhí)行過程實際上是在后面的ModelFactory.initModel()方法中進行。這里我們直接閱讀該方法的源碼:

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

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

// 這里首先獲取目標(biāo)handler執(zhí)行所需的參數(shù)中與@SessionAttribute同名或同類型的參數(shù),
// 也就是handler想要直接從@SessionAttribute中聲明的參數(shù)中獲取的參數(shù)。然后對這些參數(shù)
// 進行遍歷,首先判斷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聲明的參數(shù)的存在;②調(diào)用使用@ModelAttribute標(biāo)注的方法。我們直接閱讀invokeModelAttributeMethods()方法的源碼:

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

while (!this.modelMethods.isEmpty()) {
// 這里getNextModelMethod()方法始終會獲取modelMethods中的第0號為的方法,
// 后續(xù)該方法執(zhí)行完了之后則會將該方法從modelMethods移除掉,因而這里while
// 循環(huán)只需要判斷modelMethods是否為空即可
InvocableHandlerMethod modelMethod = 
getNextModelMethod(container).getHandlerMethod();
// 獲取當(dāng)前方法中標(biāo)注的ModelAttribute屬性,然后判斷當(dāng)前request中是否有與該屬性中name字段
// 標(biāo)注的值相同的屬性,如果存在,并且當(dāng)前ModelAttribute設(shè)置了不對該屬性進行綁定,那么
// 就直接略過當(dāng)前方法的執(zhí)行
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對方法參數(shù)進行處理,并且調(diào)用目標(biāo)方法
Object returnValue = modelMethod.invokeForRequest(request, container);

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

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

這里調(diào)用使用@ModelAttribute標(biāo)注的方法的方式比較簡單,主要需要注意的是,對于調(diào)用結(jié)果,如果當(dāng)前request中沒有同名的參數(shù),則會將調(diào)用結(jié)果添加到ModelAndViewContainer中,以供給后續(xù)handler使用。

在調(diào)用完使用上述方法之后,Spring會判斷當(dāng)前handler的返回值是否為WebAsyncTask或DefferredResult類型,如果是這兩種類型的一種,那么就會將這些任務(wù)放入一個線程池中進行異步調(diào)用,而當(dāng)前線程則可以繼續(xù)進行請求的分發(fā)。這里這種設(shè)計的目的是,默認(rèn)情況下Spring處理請求都是同步的,也就是說進行請求分發(fā)的線程是會調(diào)用用戶所聲明的handler方法的,那么如果用戶聲明的handler執(zhí)行時間較長,就可能導(dǎo)致Spring用于請求處理的線程都耗在了處理這些業(yè)務(wù)代碼上,也就導(dǎo)致后續(xù)的請求必須等待,這在高并發(fā)的場景中是不能被允許的,因而這里Spring提供了一種異步任務(wù)處理的方式,也就是進行請求分發(fā)的線程只需要將用戶的業(yè)務(wù)任務(wù)放到線程池中執(zhí)行即可,其自身可以繼續(xù)進行其他的請求的分發(fā)。如果線程池中的任務(wù)處理完成,其會通知Spring將處理結(jié)果返回給調(diào)用方。關(guān)于異步任務(wù)的處理流程,我們后面會使用專門的章節(jié)進行講解,這里只是簡單的講解其主要功能。

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

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

// 對目標(biāo)handler的參數(shù)進行處理,并且調(diào)用目標(biāo)handler
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 設(shè)置相關(guān)的返回狀態(tài)
setResponseStatus(webRequest);

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

mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 遍歷當(dāng)前容器中所有ReturnValueHandler,判斷哪種handler支持當(dāng)前返回值的處理,
// 如果支持,則使用該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的調(diào)用過程,這里主要分為三個步驟:①處理請求參數(shù)進行處理,將request中的參數(shù)封裝為當(dāng)前handler的參數(shù)的形式;②通過反射調(diào)用當(dāng)前handler;③對方法的返回值進行處理,以將其封裝為一個ModleAndView對象。這里第一步和第二步封裝在了invokeForRequest()方法中,我們首先看該方法的源碼:

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

// 將request中的參數(shù)轉(zhuǎn)換為當(dāng)前handler的參數(shù)形式
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), 
getBeanType()) + "' with arguments " + Arrays.toString(args));
}
// 這里doInvoke()方法主要是結(jié)合處理后的參數(shù),使用反射對目標(biāo)方法進行調(diào)用
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), 
getBeanType()) + "] returned [" + returnValue + "]");
}
return returnValue;
}

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

// 獲取當(dāng)前handler所聲明的所有參數(shù),主要包括參數(shù)名,參數(shù)類型,參數(shù)位置,所標(biāo)注的注解等等屬性
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是調(diào)用方提供的參數(shù),這里主要是判斷這些參數(shù)中是否有當(dāng)前類型
// 或其子類型的參數(shù),如果有,則直接使用調(diào)用方提供的參數(shù),對于請求處理而言,默認(rèn)情況下,
// 調(diào)用方提供的參數(shù)都是長度為0的數(shù)組
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}

// 如果在調(diào)用方提供的參數(shù)中不能找到當(dāng)前類型的參數(shù)值,則遍歷Spring容器中所有的
// ArgumentResolver,判斷哪種類型的Resolver支持對當(dāng)前參數(shù)的解析,這里的判斷
// 方式比較簡單,比如RequestParamMethodArgumentResolver就是判斷當(dāng)前參數(shù)
// 是否使用@RequestParam注解進行了標(biāo)注
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
// 如果能夠找到對當(dāng)前參數(shù)進行處理的ArgumentResolver,則調(diào)用其
// resolveArgument()方法從request中獲取對應(yīng)的參數(shù)值,并且進行轉(zhuǎn)換
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;
}
}

// 如果進行了參數(shù)處理之后當(dāng)前參數(shù)還是為空,則拋出異常
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;
}

關(guān)于handler的調(diào)用,可以看到,這里的實現(xiàn)也是比較簡單的,首先是遍歷所有的參數(shù),并且查找哪種ArgumentResolver能夠處理當(dāng)前參數(shù),找到了則按照具體的Resolver定義的方式進行處理即可。在所有的參數(shù)處理完成之后,RequestMappingHandlerAdapter就會使用反射調(diào)用目標(biāo)handler。

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

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

// 獲取能夠處理當(dāng)前返回值的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);
}

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

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

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

感謝各位的閱讀!關(guān)于“Spring MVC之RequestMappingHandlerAdapter的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI