Java?spring?mvc請求詳情介紹
前言:
- 本文源碼基于
spring-framework-5.3.10
。 mvc
是spring
源碼中的一個子模塊!
一、源碼執(zhí)行流程
- 用戶發(fā)送請求至前端控制器DispatcherServlet。
DispatcherServlet
收到請求調(diào)用處理器映射器HandlerMapping
。處理器映射器根據(jù)請求url找到具體的處理器,生成處理器執(zhí)行鏈HandlerExecutionChain
(包括處理器對象和處理器攔截器)一并返回給DispatcherServlet。DispatcherServlet
根據(jù)處理器Handler獲取處理器適配器HandlerAdapter
,執(zhí)行HandlerAdapter處理一系列的操作,如:參數(shù)封裝,數(shù)據(jù)格式轉(zhuǎn)換,數(shù)據(jù)驗證等操作- 執(zhí)行處理器Handler(Controller,也叫頁面控制器)。Handler執(zhí)行完成返回
ModelAndView
、HandlerAdapter
將Handler執(zhí)行結(jié)果ModelAndView返回到DispatcherServlet。 DispatcherServlet
將ModelAndView
傳給ViewReslover視圖解析器。ViewReslover解析后返回具體ViewDispatcherServlet
對View進行渲染視圖(即將模型數(shù)據(jù)model填充至視圖中)。- DispatcherServlet響應(yīng)用戶。
二、源碼執(zhí)行流程圖
三、spring mvc中的一核心組件
- DispatcherServlet: 前端調(diào)度器 , 負責(zé)將請求攔截下來分發(fā)到各控制器方法中
- HandlerMapping: 負責(zé)根據(jù)請求的URL和配置@RequestMapping映射去匹配, 匹配到會返回Handler(具體控制器的方法)
- HandlerAdaper: 負責(zé)調(diào)用Handler-具體的方法- 返回視圖的名字 Handler將它封裝到ModelAndView(封裝視圖名,request域的數(shù)據(jù))
- ViewReslover: 根據(jù)ModelAndView里面的視圖名地址去找到具體的jsp封裝在View對象中
- View:進行視圖渲染(將jsp轉(zhuǎn)換成html內(nèi)容 --這是Servlet容器的事情了) 最終response到的客戶端
四、源碼分析
/** * 最核心的控制器 * 源碼位置:org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse) */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 驗證是不是上傳的請求,上傳的請求會轉(zhuǎn)化為MultipartHttpServletRequest processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 進行映射 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 找到最合適的HandlerAdapter,按照順序,那個先解析到就是那個 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. HTTP緩存相關(guān) String method = request.getMethod(); boolean isGet = HttpMethod.GET.matches(method); if (isGet || HttpMethod.HEAD.matches(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 前置攔截器 if (!mappedHandler.applyPreHandle(processedRequest, response)) { // 返回false就不進行后續(xù)處理了 return; } // Actually invoke the handler. // 獲取參數(shù),執(zhí)行方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 如果mv有 視圖沒有,給你設(shè)置默認視圖 applyDefaultViewName(processedRequest, mv); //后置攔截器 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 渲染視圖 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
五、獲取組件相關(guān)邏輯:
原理:誰先解析到就用誰!
/** * 獲取處理器映射器 * 源碼位置:org.springframework.web.servlet.DispatcherServlet.getHandler(HttpServletRequest) */ protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { /** 拿到所有handlerMappings (容器啟動階段初始化:拿到所有實現(xiàn)了HandlerMapping的Bean) * @see DispatcherServlet#initHandlerMappings * 測試發(fā)現(xiàn): 不同的HandlerMapping可以有相同path, 誰先解析到就用哪個 * */ for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; } /** * 獲取處理器適配器 * 源碼位置:org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(Object) */ 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"); }
六、獲取參數(shù),執(zhí)行方法源碼分析
/** * 獲取參數(shù),執(zhí)行方法最外層的調(diào)用 * 源碼位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(HttpServletRequest, HttpServletResponse, Object) */ public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 直接調(diào)用這個方法 return handleInternal(request, response, (HandlerMethod) handler); } /** * 獲取參數(shù),執(zhí)行方法內(nèi)部的調(diào)用邏輯 * 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod) */ protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; // 檢查當前請求的method是否為支持的method(默認Null,可通過繼承AbstractController設(shè)置supportedMethods) // 檢查當前請求是否必須session (默認false,可通過繼承AbstractController設(shè)置requireSession) checkRequest(request); /** * 判斷當前是否需要支持在同一個session中只能線性地處理請求:一個session同時只能處理一個線程 * 因為鎖是通過 synchronized 是 JVM 進程級,所以在分布式環(huán)境下, * 無法達到同步相同 Session 的功能。默認情況下,synchronizeOnSession 為 false */ if (this.synchronizeOnSession) { // 獲取當前請求的session對象 HttpSession session = request.getSession(false); if (session != null) { // 為當前session生成一個唯一的可以用于鎖定的key Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { // 對HandlerMethod進行參數(shù)等的適配處理,并調(diào)用目標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,則為其設(shè)置過期時間。 // 這里SessionAttribute主要是通過@SessionAttribute注解生成的 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { // 如果當前不存在SessionAttributes,則判斷當前是否存在Cache-Control設(shè)置, // 如果存在,則按照該設(shè)置進行response處理,如果不存在,則設(shè)置response中的 // Cache的過期時間為-1,即立即失效 prepareResponse(response); } } return mav; } /** * 獲取參數(shù),執(zhí)行方法前的準備邏輯 * 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod) */ protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { // 把我們的請求req resp包裝成 ServletWebRequest ServletWebRequest webRequest = new ServletWebRequest(request, response); try { // 獲取容器中全局配置的InitBinder和當前HandlerMethod所對應(yīng)的Controller中 // 配置的InitBinder,用于進行參數(shù)的綁定 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // 獲取容器中全局配置的ModelAttribute和當前HandlerMethod所對應(yīng)的Controller 中配置的ModelAttribute, // 這些配置的方法將會在目標方法調(diào)用之前進行調(diào)用 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); // 封裝handlerMethod,會在調(diào)用前解析參數(shù)、調(diào)用后對返回值進行處理 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { // 讓invocableMethod擁有參數(shù)解析能力 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { // 讓invocableMethod擁有返回值處理能力 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } // 讓invocableMethod擁有InitBinder解析能力 invocableMethod.setDataBinderFactory(binderFactory); // 設(shè)置ParameterNameDiscoverer,該對象將按照一定的規(guī)則獲取當前參數(shù)的名稱 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); // ModelAndView處理容器 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); // 將request的Attribute復(fù)制一份到ModelMap mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); // *調(diào)用我們標注了@ModelAttribute的方法,主要是為我們的目標方法預(yù)加載 modelFactory.initModel(webRequest, mavContainer, invocableMethod); // 重定向的時候,忽略model中的數(shù)據(jù) 默認false mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); // 獲取當前的AsyncWebRequest,這里AsyncWebRequest的主要作用是用于判斷目標 // handler的返回值是否為WebAsyncTask或DeferredResult,如果是這兩種中的一種, // 則說明當前請求的處理應(yīng)該是異步的。所謂的異步,指的是當前請求會將Controller中 // 封裝的業(yè)務(wù)邏輯放到一個線程池中進行調(diào)用,待該調(diào)用有返回結(jié)果之后再返回到response中。 // 這種處理的優(yōu)點在于用于請求分發(fā)的線程能夠解放出來,從而處理更多的請求,提高吞吐。 // 只有待目標任務(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); // 這里就是用于判斷當前請求是否有異步任務(wù)結(jié)果的,如果存在,則對異步任務(wù)結(jié)果進行封裝 if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]"; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } // *對請求參數(shù)進行處理,調(diào)用目標HandlerMethod,并且將返回值封裝為一個ModelAndView對象 invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } // 對封裝的ModelAndView進行處理,主要是判斷當前請求是否進行了重定向,如果進行了重定向, // 還會判斷是否需要將FlashAttributes封裝到新的請求中 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); } } /** * 獲取參數(shù),執(zhí)行方法 * 源碼位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...) */ public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { /*真正的調(diào)用我們的目標對象 很重要 很重要*/ 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()) { disableContentCachingIfNecessary(webRequest); mavContainer.setRequestHandled(true); return; } } // 如果請求失敗,但是有錯誤原因,那么也會設(shè)置requestHandled屬性 else if (StringUtils.hasText(getResponseStatusReason())) { 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(formatErrorForReturnValue(returnValue), ex); } throw ex; } }
七、渲染視圖邏輯
/** * 渲染視圖邏輯 * 源碼位置:org.springframework.web.servlet.DispatcherServlet.processDispatchResult(HttpServletRequest, HttpServletResponse, HandlerExecutionChain, ModelAndView, Exception) */ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 異常視圖 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { // 解析、渲染視圖:解析視圖名,拼接前后綴 render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // Exception (if any) is already handled.. 攔截器:AfterCompletion mappedHandler.triggerAfterCompletion(request, response, null); } }
到此這篇關(guān)于Java spring mvc請求詳情介紹的文章就介紹到這了,更多相關(guān)spring mvc請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot全局配置long轉(zhuǎn)String丟失精度問題解決方案
這篇文章主要介紹了SpringBoot全局配置long轉(zhuǎn)String丟失精度問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08Java基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習(xí)題(分享)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06微信小程序調(diào)用微信登陸獲取openid及java做為服務(wù)端示例
這篇文章主要介紹了微信小程序調(diào)用微信登陸獲取openid及java做為服務(wù)端示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01Spring Boot命令行啟動添加參數(shù)的三種方式
在命令行中,常見的參數(shù)可以分為三類:選項參數(shù)、非選項參數(shù)和系統(tǒng)參數(shù),本文就來介紹一下Spring Boot命令行三種參數(shù)形式,感興趣的可以了解一下2023-09-09Mybatis-Plus中and()和or()的使用與原理詳解
最近發(fā)現(xiàn)MyBatisPlus還是挺好用的,下面這篇文章主要給大家介紹了關(guān)于Mybatis-Plus中and()和or()的使用與原理的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-09-09java不同版本在多線程中使用隨機數(shù)生成器的實現(xiàn)
本文主要介紹了java不同版本在多線程中使用隨機數(shù)生成器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04