前面鬆哥和你們聊了 DispatcherServlet 的父類 FrameworkServlet,你們從中瞭解到在 DispatcherServlet 中,方法執行的入口應該是 doService。若是小夥伴們還沒看前面的分析,能夠先看下,這有助於理解本文,傳送門SpringMVC 源碼分析之 FrameworkServlet。java
即便你沒看過 DispatcherServlet 的源碼,估計也據說過:DispatcherServlet 是 SpringMVC 的大腦,它負責整個 SpringMVC 的調度工做,是 SpringMVC 中最最核心的類,SpringMVC 整個頂層架構設計都體如今這裏,因此搞明白 DispatcherServlet 的源碼,基本上 SpringMVC 的工做原理也就瞭然於胸了。ios
通過上篇文章的分析,你們已經知道 DispatcherServlet 的入口方法是 doService,因此今天咱們就從 doService 方法開始看起,鬆哥將帶領你們,一步一步揭開 DispatcherServlet 的面紗。瀏覽器
先來看 doService,把源碼先貼上來,而後咱們逐步分析:架構
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } RequestPath previousRequestPath = null; if (this.parseRequestPath) { previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE); ServletRequestPathUtils.parseAndCache(request); } try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request); } }
這裏的代碼並不長,咱們來稍微分析一下:app
因此說這段代碼並不難理解,它的核心在於 doDispatch 方法,因此接下來咱們就來看看 doDispatch 方法。異步
doDispatch 方法所作的事情就比較多了,咱們來看下:async
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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } 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); } } } }
這個方法比較長,涉及到不少組件的處理,這裏鬆哥先和你們把思路梳理暢通,各個組件的詳細用法鬆哥將在之後的文章中和你們仔細分享。源碼分析
doDispatch 方法其實主要作了兩方面的事情:請求處理以及頁面渲染,咱們先來看看初始變量的含義:post
接下來再來看看具體的處理邏輯:this
ha.handle
去調用真正的請求,獲取到返回結果 mv。這是 doDispatch 方法的一個大體執行邏輯,doDispatch 裏邊的 try-catch 有兩層,最裏邊那一層,拋出來的異常會被賦值給 dispatchException 變量,這些異常最終在 processDispatchResult 方法中被處理掉,外面的異常則是 processDispatchResult 方法在執行的過程當中拋出的異常,通常來講主要是頁面渲染時候的異常。
最後咱們再來看下 processDispatchResult 方法的執行邏輯:
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) { 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 (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // Exception (if any) is already handled.. mappedHandler.triggerAfterCompletion(request, response, null); } }
能夠看到,在 processDispatchResult 方法中首先對異常進行了處理,配置好異常對應的 ModelAndView,而後調用 render 方法對頁面進行渲染,最後經過 triggerAfterCompletion 方法去觸發攔截器的 afterCompletion 方法。
至此,咱們就把一個請求的大體流程和你們梳理完了,鬆哥畫了一張流程圖咱們一塊兒來看下:
這下相信你們對 doDispatch 方法比較熟悉了,固然這裏還涉及到不少組件,這些組件鬆哥將在後面的文章中和你們逐一進行分析。