Spring MVC學(xué)習(xí)教程之RequestMappingHandlerAdapter詳解
前言
RequestMappingHandlerAdapter實(shí)現(xiàn)了HandlerAdapter接口,顧名思義,表示handler的adapter,這里的handler指的是Spring處理具體請(qǐng)求的某個(gè)Controller的方法,也就是說(shuō)HandlerAdapter指的是將當(dāng)前請(qǐng)求適配到某個(gè)Handler的處理器。RequestMappingHandlerAdapter是HandlerAdapter的一個(gè)具體實(shí)現(xiàn),主要用于將某個(gè)請(qǐng)求適配給@RequestMapping類(lèi)型的Handler處理。
如下是HandlerMapping接口的聲明:
public interface HandlerAdapter { // 用于判斷當(dāng)前HandlerAdapter是否能夠處理當(dāng)前請(qǐng)求 boolean supports(Object handler); // 如果當(dāng)前HandlerAdapter能夠用于適配當(dāng)前請(qǐng)求,那么就會(huì)處理當(dāng)前請(qǐng)求中 // 諸如參數(shù)和返回值等信息,以便能夠直接委托給具體的Handler處理 ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; // 獲取當(dāng)前請(qǐng)求的最后更改時(shí)間,主要用于供給瀏覽器判斷當(dāng)前請(qǐng)求是否修改過(guò), // 從而判斷是否可以直接使用之前緩存的結(jié)果 long getLastModified(HttpServletRequest request, Object handler); }
1. supports()
HandlerAdapter.supports()方法的主要作用在于判斷當(dāng)前的HandlerAdapter是否能夠支持當(dāng)前的handler的適配。這里的handler指的是某個(gè)Controller的方法,其是由HandlerExecutionChain HandlerMapping.getHandler(HttpServletRequest)方法獲取到的。從這里可以看出,HandlerMapping的作用主要是根據(jù)request請(qǐng)求獲取能夠處理當(dāng)前request的handler,而HandlerAdapter的作用在于將request中的各個(gè)屬性,如request param適配為handler能夠處理的形式。
關(guān)于HandlerAdapter.supports()方法,有這個(gè)方法的主要原因是,HandlerMapping是可以有多種實(shí)現(xiàn)的,Spring會(huì)遍歷這些具體的實(shí)現(xiàn)類(lèi),判斷哪一個(gè)能夠根據(jù)當(dāng)前request產(chǎn)生一個(gè)handler,因而對(duì)于HandlerAdapter而言,其是不知道當(dāng)前獲取到的handler具體是什么形式的,不同的HandlerMapping產(chǎn)生的handler形式是不一樣的,比如RequestMappingHandlerMapping產(chǎn)生的handler則是封裝在HandlerMethod對(duì)象中的,因而這里HandlerAdapter需要一個(gè)方法能夠快速過(guò)濾掉當(dāng)前產(chǎn)生的handler是否為其能夠進(jìn)行適配的,這個(gè)方法就是HandlerAdapter.supports()方法。如下是該方法的實(shí)現(xiàn):
// AbstractHandlerMethodAdapter @Override public final boolean supports(Object handler) { // 判斷當(dāng)前handler是否為HandlerMethod類(lèi)型,并且判斷supportsInternal()方法返回值是否為true, // 這里supportsInternal()方法是提供給子類(lèi)實(shí)現(xiàn)的一個(gè)方法,對(duì)于RequestMappingHandlerAdapter // 而言,其返回值始終是true,因?yàn)槠渲恍枰幚淼膆andler是HandlerMethod類(lèi)型的即可 return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
// RequestMappingHandlerAdapter @Override protected boolean supportsInternal(HandlerMethod handlerMethod) { // 這里RequestMappingHandlerAdapter只是對(duì)supportsInternal()返回true,因?yàn)槠渲恍枰? // 處理的handler類(lèi)型是HandlerMethod類(lèi)型即可 return true; }
2. handle()
在supports()方法判斷了所處理的handler是HandlerMethod類(lèi)型之后,RequestMappingHandlerAdapter就會(huì)調(diào)用handle()方法處理當(dāng)前請(qǐng)求。該方法的主要作用在于有五點(diǎn):
- 獲取當(dāng)前Spring容器中在方法上配置的標(biāo)注了@ModelAttribute但是沒(méi)標(biāo)注@RequestMapping注解的方法,在真正調(diào)用具體的handler之前會(huì)將這些方法依次進(jìn)行調(diào)用;
- 獲取當(dāng)前Spring容器中標(biāo)注了@InitBinder注解的方法,調(diào)用這些方法以對(duì)一些用戶(hù)自定義的參數(shù)進(jìn)行轉(zhuǎn)換并且綁定;
- 根據(jù)當(dāng)前handler的方法參數(shù)標(biāo)注的注解類(lèi)型,如@RequestParam,@ModelAttribute等,獲取其對(duì)應(yīng)的ArgumentResolver,以將request中的參數(shù)轉(zhuǎn)換為當(dāng)前方法中對(duì)應(yīng)注解的類(lèi)型;
- 配合轉(zhuǎn)換而來(lái)的參數(shù),通過(guò)反射調(diào)用具體的handler方法;
- 通過(guò)ReturnValueHandler對(duì)返回值進(jìn)行適配,比如ModelAndView類(lèi)型的返回值就由ModelAndViewMethodReturnValueHandler處理,最終將所有的處理結(jié)果都統(tǒng)一封裝為一個(gè)ModelAndView類(lèi)型的返回值,這也是RequestMappingHandlerAdapter.handle()方法的返回值類(lèi)型。
這里我們首先看看RequestMappingHandlerAdapter.handle()方法的實(shí)現(xiàn)源碼:
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // 判斷當(dāng)前是否需要支持在同一個(gè)session中只能線(xiàn)性地處理請(qǐng)求 if (this.synchronizeOnSession) { // 獲取當(dāng)前請(qǐng)求的session對(duì)象 HttpSession session = request.getSession(false); if (session != null) { // 為當(dāng)前session生成一個(gè)唯一的可以用于鎖定的key Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { // 對(duì)HandlerMethod進(jìn)行參數(shù)等的適配處理,并調(diào)用目標(biāo)handler mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // 如果當(dāng)前不存在session,則直接對(duì)HandlerMethod進(jìn)行適配 mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // 如果當(dāng)前不需要對(duì)session進(jìn)行同步處理,則直接對(duì)HandlerMethod進(jìn)行適配 mav = invokeHandlerMethod(request, response, handlerMethod); } // 判斷當(dāng)前請(qǐng)求頭中是否包含Cache-Control請(qǐng)求頭,如果不包含,則對(duì)當(dāng)前response進(jìn)行處理, // 為其設(shè)置過(guò)期時(shí)間 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { // 如果當(dāng)前SessionAttribute中存在配置的attributes,則為其設(shè)置過(guò)期時(shí)間。 // 這里SessionAttribute主要是通過(guò)@SessionAttribute注解生成的 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { // 如果當(dāng)前不存在SessionAttributes,則判斷當(dāng)前是否存在Cache-Control設(shè)置, // 如果存在,則按照該設(shè)置進(jìn)行response處理,如果不存在,則設(shè)置response中的 // Cache的過(guò)期時(shí)間為-1,即立即失效 prepareResponse(response); } } return mav; }
上述代碼主要做了兩部分處理:①判斷當(dāng)前是否對(duì)session進(jìn)行同步處理,如果需要,則對(duì)其調(diào)用進(jìn)行加鎖,不需要?jiǎng)t直接調(diào)用;②判斷請(qǐng)求頭中是否包含Cache-Control請(qǐng)求頭,如果不包含,則設(shè)置其Cache立即失效??梢钥吹?,對(duì)于HandlerMethod的具體處理是在invokeHandlerMethod()方法中進(jìn)行的,如下是該方法的具體實(shí)現(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所對(duì)應(yīng)的Controller中 // 配置的InitBinder,用于進(jìn)行參數(shù)的綁定 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // 獲取容器中全局配置的ModelAttribute和當(dāng)前當(dāng)前HandlerMethod所對(duì)應(yīng)的Controller // 中配置的ModelAttribute,這些配置的方法將會(huì)在目標(biāo)方法調(diào)用之前進(jìn)行調(diào)用 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 將handlerMethod封裝為一個(gè)ServletInvocableHandlerMethod對(duì)象, // 該對(duì)象用于對(duì)當(dāng)前request的整體調(diào)用流程進(jìn)行了封裝 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,該對(duì)象將按照一定的規(guī)則獲取當(dāng)前參數(shù)的名稱(chēng) 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,如果是這兩種中的一種, // 則說(shuō)明當(dāng)前請(qǐng)求的處理應(yīng)該是異步的。所謂的異步,指的是當(dāng)前請(qǐng)求會(huì)將Controller中 // 封裝的業(yè)務(wù)邏輯放到一個(gè)線(xiàn)程池中進(jìn)行調(diào)用,待該調(diào)用有返回結(jié)果之后再返回到response中。 // 這種處理的優(yōu)點(diǎn)在于用于請(qǐng)求分發(fā)的線(xiàn)程能夠解放出來(lái),從而處理更多的請(qǐng)求,只有待目標(biāo)任務(wù) // 完成之后才會(huì)回來(lái)將該異步任務(wù)的結(jié)果返回。 AsyncWebRequest asyncWebRequest = WebAsyncUtils .createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); // 封裝異步任務(wù)的線(xiàn)程池,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)前請(qǐng)求是否有異步任務(wù)結(jié)果的,如果存在,則對(duì)異步任務(wù)結(jié)果進(jìn)行封裝 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é)果,雖然封裝的是一個(gè)HandlerMethod,但只是Spring簡(jiǎn)單的封裝 // 的一個(gè)Callable對(duì)象,該對(duì)象中直接將調(diào)用結(jié)果返回了。這樣封裝的目的在于能夠統(tǒng)一的 // 進(jìn)行右面的ServletInvocableHandlerMethod.invokeAndHandle()方法的調(diào)用 invocableMethod = invocableMethod.wrapConcurrentResult(result); } // 對(duì)請(qǐng)求參數(shù)進(jìn)行處理,調(diào)用目標(biāo)HandlerMethod,并且將返回值封裝為一個(gè)ModelAndView對(duì)象 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 對(duì)封裝的ModelAndView進(jìn)行處理,主要是判斷當(dāng)前請(qǐng)求是否進(jìn)行了重定向,如果進(jìn)行了重定向, // 還會(huì)判斷是否需要將FlashAttributes封裝到新的請(qǐng)求中 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { // 調(diào)用request destruction callbacks和對(duì)SessionAttributes進(jìn)行處理 webRequest.requestCompleted(); } }
上述代碼是RequestMappingHandlerAdapter處理請(qǐng)求的主要流程,其主要包含四個(gè)部分:①獲取當(dāng)前容器中使用@InitBinder注解注冊(cè)的屬性轉(zhuǎn)換器;②獲取當(dāng)前容器中使用@ModelAttribute標(biāo)注但沒(méi)有使用@RequestMapping標(biāo)注的方法,并且在調(diào)用目標(biāo)方法之前調(diào)用這些方法;③判斷目標(biāo)handler返回值是否使用了WebAsyncTask或DefferredResult封裝,如果封裝了,則按照異步任務(wù)的方式進(jìn)行執(zhí)行;④處理請(qǐng)求參數(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中進(jìn)行掃描獲取 Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { // 在當(dāng)前bean中查找所有標(biāo)注了@InitBinder注解的方法,這里INIT_BINDER_METHODS就是一個(gè) // 選擇器,表示只獲取使用@InitBinder標(biāo)注的方法 methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } // 這里initBinderAdviceCache是在RequestMappingHandlerAdapter初始化時(shí)同步初始化的, // 其內(nèi)包含的方法有如下兩個(gè)特點(diǎn):①當(dāng)前方法所在類(lèi)使用@ControllerAdvice進(jìn)行標(biāo)注了; // ②當(dāng)前方法使用@InitBinder進(jìn)行了標(biāo)注。也就是說(shuō)其內(nèi)保存的方法可以理解為是全局類(lèi)型 // 的參數(shù)綁定方法 List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>(); this.initBinderAdviceCache.forEach((clazz, methodSet) -> { // 這里判斷的是當(dāng)前配置的全局類(lèi)型的InitBinder是否能夠應(yīng)用于當(dāng)前bean, // 判斷的方式主要在@ControllerAdvice注解中進(jìn)行了聲明,包括通過(guò)包名,類(lèi)所在的包, // 接口或者注解的形式限定的范圍 if (clazz.isApplicableToBeanType(handlerType)) { Object bean = clazz.resolveBean(); for (Method method : methodSet) { initBinderMethods.add(createInitBinderMethod(bean, method)); } } }); // 這里是將當(dāng)前HandlerMethod所在bean中的InitBinder添加到需要執(zhí)行的initBinderMethods中。 // 這里從添加的順序可以看出,全局類(lèi)型的InitBinder會(huì)在當(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,全局類(lèi)型的InitBinder需要聲明的類(lèi)上使用@ControllerAdvice進(jìn)行標(biāo)注,并且聲明方法上使用@InitBinder進(jìn)行標(biāo)注;另一種則是獲取當(dāng)前handler所在類(lèi)中的使用@InitBinder注解標(biāo)注的方法。這兩種InitBinder都會(huì)執(zhí)行,只不過(guò)全局類(lèi)型的InitBinder會(huì)先于局部類(lèi)型的InitBinder執(zhí)行。關(guān)于使用@InitBinder標(biāo)注的方法的執(zhí)行時(shí)間點(diǎn),需要說(shuō)明的是,因?yàn)槠渑c參數(shù)綁定有關(guān),因而其只會(huì)在參數(shù)綁定時(shí)才會(huì)執(zhí)行。
這里我們繼續(xù)看RequestMappingHandlerAdapter是如何獲取@ModelAttribute標(biāo)注的方法并且執(zhí)行的,如下是getModelFactory()方法的源碼:
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { // 這里SessionAttributeHandler的作用是聲明幾個(gè)屬性,使其能夠在多個(gè)請(qǐng)求之間共享, // 并且其能夠保證當(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) { // 如果緩存中沒(méi)有相關(guān)屬性,那么就在當(dāng)前bean中查找所有使用@ModelAttribute標(biāo)注,但是 // 沒(méi)有使用@RequestMapping標(biāo)注的方法,并將這些方法緩存起來(lái) methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); this.modelAttributeCache.put(handlerType, methods); } // 獲取全局的使用@ModelAttribute標(biāo)注,但是沒(méi)有使用@RequestMapping標(biāo)注的方法, // 這里全局類(lèi)型的方法的聲明方式需要注意的是,其所在的bean必須使用@ControllerAdvice進(jìn)行標(biāo)注 List<InvocableHandlerMethod> attrMethods = new ArrayList<>(); this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> { // 判斷@ControllerAdvice中指定的作用的bean范圍與當(dāng)前bean是否匹配,匹配了才會(huì)對(duì)其應(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中。從這里的添加順序 // 可以看出,全局類(lèi)型的方法將會(huì)先于局部類(lèi)型的方法執(zhí)行 for (Method method : methods) { Object bean = handlerMethod.getBean(); attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } // 將需要執(zhí)行的方法等數(shù)據(jù)封裝為ModelFactory對(duì)象 return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); }
上述getModelFactory()方法主要工作還是獲取當(dāng)前需要先于目標(biāo)handler執(zhí)行的方法,并且獲取的方式與前面的InitBinder非常的相似,這里就不再贅述。關(guān)于這里獲取的方法,其具體的執(zhí)行過(guò)程實(shí)際上是在后面的ModelFactory.initModel()方法中進(jìn)行。這里我們直接閱讀該方法的源碼:
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同名或同類(lèi)型的參數(shù), // 也就是handler想要直接從@SessionAttribute中聲明的參數(shù)中獲取的參數(shù)。然后對(duì)這些參數(shù) // 進(jìn)行遍歷,首先判斷request中是否包含該屬性,如果不包含,則從之前的SessionAttribute緩存 // 中獲取,如果兩個(gè)都沒(méi)有,則直接拋出異常 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()方法始終會(huì)獲取modelMethods中的第0號(hào)為的方法, // 后續(xù)該方法執(zhí)行完了之后則會(huì)將該方法從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è)置了不對(duì)該屬性進(jìn)行綁定,那么 // 就直接略過(guò)當(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; } // 通過(guò)ArgumentResolver對(duì)方法參數(shù)進(jìn)行處理,并且調(diào)用目標(biāo)方法 Object returnValue = modelMethod.invokeForRequest(request, container); // 如果當(dāng)前方法的返回值不為空,則判斷當(dāng)前@ModelAttribute是否設(shè)置了需要綁定返回值, // 如果設(shè)置了,則將返回值綁定到請(qǐng)求中,后續(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)注的方法的方式比較簡(jiǎn)單,主要需要注意的是,對(duì)于調(diào)用結(jié)果,如果當(dāng)前request中沒(méi)有同名的參數(shù),則會(huì)將調(diào)用結(jié)果添加到ModelAndViewContainer中,以供給后續(xù)handler使用。
在調(diào)用完使用上述方法之后,Spring會(huì)判斷當(dāng)前handler的返回值是否為WebAsyncTask或DefferredResult類(lèi)型,如果是這兩種類(lèi)型的一種,那么就會(huì)將這些任務(wù)放入一個(gè)線(xiàn)程池中進(jìn)行異步調(diào)用,而當(dāng)前線(xiàn)程則可以繼續(xù)進(jìn)行請(qǐng)求的分發(fā)。這里這種設(shè)計(jì)的目的是,默認(rèn)情況下Spring處理請(qǐng)求都是同步的,也就是說(shuō)進(jìn)行請(qǐng)求分發(fā)的線(xiàn)程是會(huì)調(diào)用用戶(hù)所聲明的handler方法的,那么如果用戶(hù)聲明的handler執(zhí)行時(shí)間較長(zhǎng),就可能導(dǎo)致Spring用于請(qǐng)求處理的線(xiàn)程都耗在了處理這些業(yè)務(wù)代碼上,也就導(dǎo)致后續(xù)的請(qǐng)求必須等待,這在高并發(fā)的場(chǎng)景中是不能被允許的,因而這里Spring提供了一種異步任務(wù)處理的方式,也就是進(jìn)行請(qǐng)求分發(fā)的線(xiàn)程只需要將用戶(hù)的業(yè)務(wù)任務(wù)放到線(xiàn)程池中執(zhí)行即可,其自身可以繼續(xù)進(jìn)行其他的請(qǐng)求的分發(fā)。如果線(xiàn)程池中的任務(wù)處理完成,其會(huì)通知Spring將處理結(jié)果返回給調(diào)用方。關(guān)于異步任務(wù)的處理流程,我們后面會(huì)使用專(zhuān)門(mén)的章節(jié)進(jìn)行講解,這里只是簡(jiǎn)單的講解其主要功能。
在進(jìn)行了相關(guān)前置方法調(diào)用和異步任務(wù)的判斷之后,RequestMappingHandlerAdapter就會(huì)開(kāi)始調(diào)用目標(biāo)handler了。調(diào)用過(guò)程在ServletInvocableHandlerMethod.invokeAndHandle()方法中,如下是該方法的源碼:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 對(duì)目標(biāo)handler的參數(shù)進(jìn)行處理,并且調(diào)用目標(biāo)handler Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 設(shè)置相關(guān)的返回狀態(tài) setResponseStatus(webRequest); // 如果請(qǐng)求處理完成,則設(shè)置requestHandled屬性 if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { // 如果請(qǐng)求失敗,但是有錯(cuò)誤原因,那么也會(huì)設(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; } }
對(duì)于handler的調(diào)用過(guò)程,這里主要分為三個(gè)步驟:①處理請(qǐng)求參數(shù)進(jìn)行處理,將request中的參數(shù)封裝為當(dāng)前handler的參數(shù)的形式;②通過(guò)反射調(diào)用當(dāng)前handler;③對(duì)方法的返回值進(jìn)行處理,以將其封裝為一個(gè)ModleAndView對(duì)象。這里第一步和第二步封裝在了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ù),使用反射對(duì)目標(biāo)方法進(jìn)行調(diào)用 Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; } // 本方法主要是通過(guò)當(dāng)前容器中配置的ArgumentResolver對(duì)request中的參數(shù)進(jìn)行轉(zhuǎn)化, // 將其處理為目標(biāo)handler的參數(shù)的形式 private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 獲取當(dāng)前handler所聲明的所有參數(shù),主要包括參數(shù)名,參數(shù)類(lèi)型,參數(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)前類(lèi)型 // 或其子類(lèi)型的參數(shù),如果有,則直接使用調(diào)用方提供的參數(shù),對(duì)于請(qǐng)求處理而言,默認(rèn)情況下, // 調(diào)用方提供的參數(shù)都是長(zhǎng)度為0的數(shù)組 args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } // 如果在調(diào)用方提供的參數(shù)中不能找到當(dāng)前類(lèi)型的參數(shù)值,則遍歷Spring容器中所有的 // ArgumentResolver,判斷哪種類(lèi)型的Resolver支持對(duì)當(dāng)前參數(shù)的解析,這里的判斷 // 方式比較簡(jiǎn)單,比如RequestParamMethodArgumentResolver就是判斷當(dāng)前參數(shù) // 是否使用@RequestParam注解進(jìn)行了標(biāo)注 if (this.argumentResolvers.supportsParameter(parameter)) { try { // 如果能夠找到對(duì)當(dāng)前參數(shù)進(jìn)行處理的ArgumentResolver,則調(diào)用其 // resolveArgument()方法從request中獲取對(duì)應(yīng)的參數(shù)值,并且進(jìn)行轉(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; } } // 如果進(jìn)行了參數(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)用,可以看到,這里的實(shí)現(xiàn)也是比較簡(jiǎn)單的,首先是遍歷所有的參數(shù),并且查找哪種ArgumentResolver能夠處理當(dāng)前參數(shù),找到了則按照具體的Resolver定義的方式進(jìn)行處理即可。在所有的參數(shù)處理完成之后,RequestMappingHandlerAdapter就會(huì)使用反射調(diào)用目標(biāo)handler。
對(duì)于返回值的處理,其形式與對(duì)參數(shù)的處理非常相似,都是對(duì)ReturnValueHandler進(jìn)行遍歷,判斷哪種Handler能夠支持當(dāng)前返回值的處理,如果找到了,則按照其規(guī)則進(jìn)行處理即可。如下是該過(guò)程的主要流程代碼:
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 獲取能夠處理當(dāng)前返回值的Handler,比如如果返回值是ModelAndView類(lèi)型,那么這里的handler就是 // ModelAndViewMethodReturnValueHandler HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } // 通過(guò)獲取到的handler處理返回值,并將其封裝到ModelAndViewContainer中 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } // 本方法的主要作用是獲取能夠處理當(dāng)前返回值的ReturnValueHandler private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) { // 判斷返回值是否為異步類(lèi)型的返回值,即WebAsyncTask或DefferredResult boolean isAsyncValue = isAsyncReturnValue(value, returnType); // 對(duì)所有的ReturnValueHandler進(jìn)行遍歷,判斷其是否支持當(dāng)前返回值的處理。這里如果當(dāng)前返回值 // 是異步類(lèi)型的返回值,還會(huì)判斷當(dāng)前ReturnValueHandler是否為 // AsyncHandlerMethodReturnValueHandler類(lèi)型,如果不是,則會(huì)繼續(xù)查找 for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } // 判斷是否支持返回值處理的主要位置,比如ModelAndViewMethodReturnValueHandler就會(huì) // 判斷返回值是否為ModelAndView類(lèi)型,如果是,則表示其是當(dāng)前ReturnValuleHandler所支持的類(lèi)型 if (handler.supportsReturnType(returnType)) { return handler; } } return null; }
3. 小結(jié)
本文首先講解了RequestMappingHandlerMapping所做的工作與RequestMappingHandlerAdapter的區(qū)別,然后講解RequestMappingHandlerAdapter是如何判斷當(dāng)前的handler是否為其所支持的類(lèi)型的,最后詳細(xì)講解了其是如果將request適配為目標(biāo)handler能夠調(diào)用的形式的??偟膩?lái)講,RequestMappingHandlerAdapter的主要作用就是調(diào)用RequestMappingHandlerMapping所獲取到的handler,然后將返回值封裝為一個(gè)ModelAndView對(duì)象,該對(duì)象中保存了所要渲染的視圖名稱(chēng)和渲染視圖時(shí)所需要的參數(shù)值,而具體的渲染過(guò)程則是通過(guò)View對(duì)象進(jìn)行的。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- springmvc中RequestMappingHandlerAdapter與HttpMessageConverter的裝配講解
- Spring MVC學(xué)習(xí)教程之RequestMappingHandlerMapping匹配
- Spring MVC溫故而知新系列教程之請(qǐng)求映射RequestMapping注解
- Spring Mvc中傳遞參數(shù)方法之url/requestMapping詳解
- 詳解獲取Spring MVC中所有RequestMapping以及對(duì)應(yīng)方法和參數(shù)
- Spring MVC之@RequestMapping注解詳解
- SpringMVC源碼解讀之HandlerMapping - AbstractUrlHandlerMapping系列request分發(fā)
- Springmvc RequestMapping請(qǐng)求實(shí)現(xiàn)方法解析
相關(guān)文章
有關(guān)tomcat內(nèi)存溢出的完美解決方法
下面小編就為大家?guī)?lái)一篇有關(guān)tomcat內(nèi)存溢出的完美解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05解決idea找不到setting.xml文件的問(wèn)題
這篇文章主要介紹了解決idea找不到setting.xml文件的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02SpringBoot全局異常處理與定制404頁(yè)面的方法
這篇文章主要介紹了SpringBoot全局異常處理與定制404頁(yè)面的相關(guān)資料,本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2007-09-09SpringBoot后端接口的實(shí)現(xiàn)(看這一篇就夠了)
這篇文章主要介紹了SpringBoot后端接口的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09spring mvc中@PathVariable / 帶斜杠方式獲取
這篇文章主要介紹了spring mvc中@PathVariable / 帶斜杠方式獲取,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot獲取http數(shù)據(jù)、打印HTTP參數(shù)的4種方式
Java的話(huà)本地打斷點(diǎn)可以調(diào)試獲取rest入?yún)?但是在生產(chǎn)環(huán)境可能我們獲取入?yún)ⅲ℉ttp?header/parameter)可能就沒(méi)有那么的輕松了,所以本文給大家介紹了SpringBoot獲取http數(shù)據(jù)、打印HTTP參數(shù)的4種方式,需要的朋友可以參考下2024-03-03解決IDEA導(dǎo)入javaWeb項(xiàng)目注解爆紅的問(wèn)題
這篇文章主要介紹了解決IDEA導(dǎo)入javaWeb項(xiàng)目注解爆紅的問(wèn)題,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10