您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(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é)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責(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)容。