Spring MVC請求參數的深入解析
請求參數解析
客戶端請求在handlerMapping中找到對應handler後,將會繼續執行DispatchServlet的doPatch()方法。
首先是找到handler對應的適配器。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
進入到getHandlerAdapter(mappedHandler.getHandler())
方法中
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { if (this.handlerAdapters != null) { for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) { return adapter; } } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
這裡存在多個適配器,如圖:
其中使用@RequestMaping註解修飾的控制器都將適配第一個適配器;而函數式方法將會使用第二個適配器。
跟蹤請求,這裡將會獲得第一個適配器,判斷也簡單,如下:
public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler)); }
如果是HandlerMethod類型的處理器就采用這個適配器,而客戶端請求正好對應的是HandlerMethod處理器。
找到適配器後,將會真正執行處理器邏輯。如下:
// Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
進入RequestMappingHandlerAdapter,執行適配器核心方法:
@Override protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
其核心代碼為實際執行處理器方法:
mav = invokeHandlerMethod(request, response, handlerMethod);
同樣,我們打開RequestMappingHandlerAdapter中的invokeHandlerMethod方法:
@Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { //通過處理器獲得真正的執行方法及其參數列表 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); //給執行方法對象添加參數解析器 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } //給執行方法對象添加返回值處理器 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } //處理器對象裝配完成,執行控制器方法 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } }
在這個關鍵方法中,首先執行請求對應的控制器邏輯,之後進行系列處理,根據返回值處理器處理返回值。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //執行控制器方法 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); Assert.state(this.returnValueHandlers != null, "No return value handlers"); try { //處理返回值 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
以下給出部分參數解析器及返回值處理器截圖:
參數解析器。對應每一個參數(路徑變量、矩陣變量、獲得請求頭、請求域等)的獲取方式
返回值處理器。ModelAndView、ResponseBody等。每個處理器處理不同類別的返回值類型。
接下來,真正進入到最終執行method方法invocableMethod.invokeAndHandle(webRequest, mavContainer);
,這裡是真是執行控制器中映射的方法。
以下為獲得參數列表對應值的邏輯,參數獲取完成後將會執行真正的控制器邏輯。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //通過參數解析器獲取參數列表每一個參數的值 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } return doInvoke(args); }
進入解析邏輯,解析是對比每一個參數綁定的註解,如果註解一致將會使用對應的解析器將請求傳遞的參數值獲取到。
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //獲得控制器參數列表,每一個參數包含其參數類型,參數次序、註解修飾(用於使用對應解析器) MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { //輪循獲得參數 MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } //判斷解析器是否支持當前參數的解析 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { //獲得參數值的核心方法 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }
查看解析器是否指出當前參數部分代碼,可以瞭解到SpringMVC的緩存策略。
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { //從緩存中獲取當前參數的解析器 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); //緩存中不存在則將這個參數對應的解析器加到緩存中,提升後續相同請求響應速度。 if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }
參數列表
以下為獲得的參數列表第一個參數部分屬性
其對應的是控制器中id參數:
@GetMapping(value = "/student01/{id}/car/{name}") public Map<String, Object> testAnnotation(@PathVariable(name = "id") String id){ Map<String, Object> map = new HashMap<>(); map.put("id", id); return map; }
以上即為獲得請求Handler對應的適配器,處理參數映射、執行控制器邏輯、返回值處理的核心源碼處理。
總結
到此這篇關於Spring MVC請求參數解析的文章就介紹到這瞭,更多相關SpringMVC請求參數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文搞懂SpringMVC中@InitBinder註解的使用
- SpringMVC 參數綁定相關知識總結
- springmvc path請求映射到bean 方法的流程
- SpringMVC註解之@ResponseBody註解原理
- 一文帶你揭秘SpringMvc參數值映射