中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

Spring MVC之RequestMappingHandlerAdapter的示例分析

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

這篇文章給大家分享的是有關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;
}

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

竹山县| 乐山市| 琼结县| 衡山县| 荃湾区| 广南县| 德州市| 恩施市| 濮阳市| 读书| 泸州市| 崇义县| 灵丘县| 资阳市| 庆安县| 丹巴县| 昔阳县| 泗阳县| 贺州市| 方正县| 将乐县| 罗江县| 咸阳市| 聂拉木县| 洞头县| 宜城市| 高陵县| 碌曲县| 太原市| 南皮县| 永靖县| 紫云| 兴仁县| 隆安县| 东阿县| 偏关县| 剑川县| 永州市| 景谷| 十堰市| 股票|