一、客戶端發送請求的總體過程
DispatcherServlet是SpringMVC的入口,DispatcherServlet其實也是一個Servlet。服務器處理客戶端請求的步驟如下:
1、客戶端發送請求的時候,會調用Servlet對應的doGet、doPost、doDelete等方法。
2、上面的方法會調用processRequest方法
3、processRequest方法進一步調用doService方法
4、DispatcherServlet實現了doService方法,在doService方法中對Request參數進行處理,然后調用doDispatch方法
5、在doDispatch方法中獲取並調用處理器映射器、處理器適配器,獲取並返回執行結果。
DispatcherServlet是Web中處於比較核心的位置,被稱為前端控制器。SpringMVC中常用的幾個概念,處理器映射器(HandlerMapping)、處理器適配器(HandlerAdapter)和視圖解析器(ViewResolver)都在DispatcherServlet的doDispatch中有所體現。
通過調用getHandler方法獲取Handler對象,getHandler方法會調用HandlerMapping,通過請求的路徑查找Handler;返回值是一個HandlerExecutionChain對象,其中不只包含了Handler對象,還包含一個HandlerInterceptor(攔截器)的鏈表。
Handler需要借助於HandlerAdapter來執行,doDispatch調用getHandlerAdapter方法查找具體的HandlerAdapter。Spring容器中可能配置了多個HandlerAdapter,具體哪個HandlerAdapter能夠處理當前的Handler,是根據HandlerAdapter的supports方法來查找可以處理該Handler的HandlerAdapter。
之后會調用攔截器的preHandler方法,HandlerAdapter會處理具體的Handler,調用攔截器的postHandler方法。
然后,doDispatch會調用processDispatchResult方法,在processDispatchResult方法中,在其中的render方法中,會循環ViewResolver,確定哪個ViewResolver可以解析對應view,然后調用view的render方法進行渲染。processDispatchResult方法還會調用攔截器的afterCompletion方法。
二、處理器映射器
通過調用處理器映射器,得到請求路徑對應的處理器。如果沒有找到處理器,則在Response中返回錯誤信息,該方法直接退出。
// Determine handler for the current request. mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; }
getHandler方法的代碼如下,實質就是循環便利所有處理器映射器,找到與請求路徑相匹配的處理器映射器。
/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
三、判斷是否網頁是否需要重新生成
HTTP協議允許只發送帶有Last-Modified的表頭信息到服務器端,服務器端判斷本地的信息是否修改了,如果沒修改,最后的時間將與Last-Modified一致,此時不需要服務器端再生成信息,直接告訴瀏覽器信息沒有改變,使用其本地數據即可。
// 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 (logger.isDebugEnabled()) { logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified); } if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } }
四、攔截器
在從處理器映射器獲取對應的處理器的時候(通過DispatcherServlet的getHandler方法),返回的不是處理器對象,而是一個HandlerExecutionChain,這個HandlerExecutionChain中包含Handler對象;同時還包含一個HandlerInterceptor鏈表,而HandlerInterceptor就是攔截器。
而對於HandlerInterceptor接口中定義的三個方法中,preHandler在handler的執行前被調用;而postHandler在handler執行后,在進行視圖解析之前執行;afterCompletion在view渲染完成、在DispatcherServlet返回之前執行。
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv);
上面的代碼中,mappedHandler.applyPreHandle(processedRequest, response)是執行攔截器的preHandler;而mappedHandler.applyPostHandle(processedRequest, response, mv)則執行攔截器的postHandler方法。而攔截器的afterCompletion方法則是在processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)方法中執行的,位置是在該方法的最后,view渲染完成后。
PS:我們需要注意的是:當preHandler返回false時,當前的請求將在執行完afterCompletion后直接返回,handler也將不會執行。源碼如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { if (getInterceptors() != null) { for (int i = 0; i < getInterceptors().length; i++) { HandlerInterceptor interceptor = getInterceptors()[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
需要注意的是上面的interceptorIndex變量,它保存的是剛剛已經執行過的攔截器。而一旦攔截器的preHandler返回false,就會進入triggerAfterCompletion方法,該方法會執行攔截器的afterCompletion方法。但是不可能把所有攔截器的afterCompletion方法都執行一遍,所以使用interceptorIndex進行記錄,就可以很方便的知道都有哪些攔截器執行了preHandler方法,調用他們的afterCompletion就可以了。
另外需要注意的地方是,設置interceptorIndex的位置是在循環的最后,也就是說,此處記錄的是preHandler返回true的那個攔截器對應的index,也就是說返回false的攔截器的afterCompletion方法不會被調用。
五、處理器適配器
通過HandlerExecutionChain.getHandler返回處理器對象,getHandler返回的是Object對象。DispatcherServlet通過getHandlerAdapter方法找到與Handler匹配的HandlerAdapter類,再通過這個HandlerAdapter去執行Handler對應的方法。
獲取Handler匹配的HandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { for (HandlerAdapter ha : this.handlerAdapters) { if (logger.isTraceEnabled()) { logger.trace("Testing handler adapter [" + ha + "]"); } if (ha.supports(handler)) { return ha; } } throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }
HandlerAdapter去執行Handler對應方法,其中返回的mv是ModelAndView對象:
try { // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } }
六、視圖解析器
DispatcherServlet在processDispatchResult中解析並返回View,在render方法中會根據ModelAndView渲染產生View對象。
