Spring MVC:DispatchServlet類

Spring MVC架構

  Spring Web MVC是基於Servlet API構建的原始Web框架,從一開始就已包含在Spring框架中。傳統的模型層被拆分爲了業務層(Service)和數據訪問層(DAO,Data Access Object)。 在 Service 下能夠經過 Spring 的聲明式事務操做數據訪問層,而在業務層上還容許咱們訪問 NoSQL ,這樣就可以知足異軍突起的 NoSQL 的使用了,它能夠大大提升互聯網系統的性能。ios

  M 表明 模型(Model),V 表明 視圖(View),C 表明 控制器(controller):web

  • 模型封裝了應用程序數據,而且一般它們由 POJO 組成。spring

  • 視圖主要用於呈現模型數據,而且一般它生成客戶端的瀏覽器能夠解釋的 HTML 輸出。設計模式

  • 控制器主要用於處理用戶請求,而且構建合適的模型並將其傳遞到視圖呈現。

一些優勢:

1.在Spring MVC中,控制器能夠處理全部HTTP方法的請求,這是RESTful Web服務的基礎。例如,能夠處理GET方法來執行讀取操做,POST方法來建立資源,PUT方法來更新資源以及DELETE方法來從服務器中刪除資源。從Spring 3.2開始,還能夠處理PATCH請求。瀏覽器

2.對於REST,數據的表示很是重要,這就是爲何Spring MVC容許您經過使用 @ResponseBody 批註和各類 HttpMessgeConverter 實現方式徹底繞過基於View的呈現  。經過使用此功能,您能夠直接向客戶端發送響應,例如,客戶端所需的資源以及客戶端所需的格式。
spring-mvc

使用Spring開發Java RESTful Web服務的7個理由

3. Spring 4.0版本添加了專用註釋,@RestController使RESTful Web服務的開發更加容易。服務器

若是使用@RestController 而不是 @Controller註釋控制器類  ,則 Spring 會將消息對話應用於控制器中的全部處理程序方法。多線程

這意味着您不須要爲每一個方法添加 @ResponseBody 註釋。這也使您的代碼更加簡潔。 架構

4. REST Web服務和普通Web應用程序之間的主要區別之一是,REST傳遞資源標識URI自己中的數據,例如  /messages/101,  而Web應用程序一般使用查詢參數,例如 /messages?Id=101。mvc

若是您還記得的話,咱們使用 @RequestParam 獲取這些查詢參數的值,可是不用擔憂,Spring MVC還提供了一個@PathVariable批註,能夠從URL提取數據。它容許控制器處理對參數化URL的請求。

5. RESTful Web服務的另外一個關鍵方面是表示,表示同一資源能夠以不一樣的格式表示,例如JSON,XML,HTML等。值得慶幸的是,Spring提供了幾種視圖實現和視圖解析器,以將數據呈現爲JSON,XML,和HTML。

例如,ContentNegotiatingViewResolver 能夠查看請求的文件擴展名或「接受」標頭,以找出客戶端資源的正確表示形式。

6.與 @ResponseBody 用於將響應轉換爲客戶端想要的格式(經過使用 HttpMessageConverts)的註釋  相似  ,Spring MVC還提供  @RequestBody 註釋,該註釋使用  HttpMethodConverter 實現將入站HTTP數據轉換爲傳遞到控制器的處理程序方法中的Java對象。

圖片標題

7.在 Spring框架還提供了一個模板類,RestTemplate,這是相似的 JdbcTemplate,而且 JmsTemplate,它能夠消耗REST資源。您可使用此類測試RESTful Web服務或開發REST客戶端。

DispatchServlet

DispatchServlet 是 HTTP 請求處理程序/控制器的中央調度程序。Spring Web 模型-視圖-控制(MVC)框架是圍繞 DispatcherServlet 設計的。

下面是對應於 DispatcherServlet 傳入 HTTP 請求的事件序列:

  • 收到一個 HTTP 請求後,DispatcherServlet 根據 HandlerMapping 來選擇而且調用適當的控制器

  • 控制器接受請求(取決於使用@Controller@RequestMapping註釋請求的URL),並基於使用的 GET 或 POST 方法來調用適當的 service 方法。Service 方法將設置基於定義的業務邏輯的模型數據,並返回視圖名稱到 DispatcherServlet 中。

  • DispatcherServlet 會從視圖解析器 ViewResolver 獲取幫助,爲請求檢取定義視圖。

  • 一旦肯定視圖,DispatcherServlet 將把模型數據傳遞給視圖,最後呈如今瀏覽器中。

  上面所提到的全部組件,即 HandlerMappingController 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是帶有一些對 web 應用程序必要的額外特性的 ApplicationContext 的擴展。

DispatcherServlet 初始化流程

DispatcherServlet在MVC子容器初始化時會初始化這9大組件:

  1. MultipartResolver:用來處理上傳請求的。其處理方式就是將 request 包裝成 MultipartHttpServletRequest。而後就能夠用 MultipartHttpServletRequest 這個直接調用 getFile 獲取的文件了。

  2. LocalResolver:做用是從 request 中解析出 local,關於local大多數狀況下都是用來作國際化處理的。

  3. ThemeResolver:解析主題的,例如配一套properties文件供系統在不一樣的時候讀取切換。

  4. HandlerMapping:其做用就是根據 request 找到相應的處理器 Handler 和 Intecepter 攔截器。

  5. HandlerAdapter:其接口的 handle 方法就是使用 handler 來處理邏輯的。

  6. HandlerExceptionResolver:Spring MVC 中的異常處理組件,根據異常設置 ModelAndView,而後再將處理結果交給 render方法進行渲染。在渲染以前工做的,所以渲染過程當中發生異常,HandlerExceptionResolver 是不會處理的。

  7. RequestToViewNameTranslator:將request請求轉換爲視圖名稱。

  8. ViewResolver:做用是將 String 類型的邏輯視圖根據 local 解析爲 View 視圖的。

  9. FlashMapManager:在redirect是進行參數傳遞須要用到。

1     /**
2      * This implementation calls {@link #initStrategies}.
3      */
4     @Override
5     protected void onRefresh(ApplicationContext context) {
6         initStrategies(context);
7     }
 1     /**
 2      * Initialize the strategy objects that this servlet uses.
 3      * <p>May be overridden in subclasses in order to initialize further strategy objects.
 4      */
 5     protected void initStrategies(ApplicationContext context) {
 6         initMultipartResolver(context);
 7         initLocaleResolver(context);
 8         initThemeResolver(context);
 9         initHandlerMappings(context);
10         initHandlerAdapters(context);
11         initHandlerExceptionResolvers(context);
12         initRequestToViewNameTranslator(context);
13         initViewResolvers(context);
14         initFlashMapManager(context);
15     }

DispatcherServlet 執行流程

  Http請求經過 HttpServlet 讀取請求的內容,生成一個 HttpServletRequest 和一個 HttpServletResponse。下面這段方法將客戶端請求分派到受保護的 service 方法。

 1     @Override
 2     public void service(ServletRequest req, ServletResponse res)
 3         throws ServletException, IOException {
 4 
 5         HttpServletRequest  request;
 6         HttpServletResponse response;
 7 
 8         try {
 9             request = (HttpServletRequest) req;
10             response = (HttpServletResponse) res;
11         } catch (ClassCastException e) {
12             throw new ServletException(lStrings.getString("http.non_http"));
13         }
14         service(request, response);
15     }

  接着來到 FrameworkServlet 的 service 方法,如果 patch 類型的方法,則單獨拿出來進行處理。

 1     /**
 2      * Override the parent class implementation in order to intercept PATCH requests.
 3      */
 4     @Override
 5     protected void service(HttpServletRequest request, HttpServletResponse response)
 6             throws ServletException, IOException {
 7 
 8         HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
 9         if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
10             processRequest(request, response);
11         }
12         else {
13             super.service(request, response);
14         }
15     }

  最後回到 HttpServlet 的 service 方法中,這裏使用到的設計模式是模板方法。

 1     protected void service(HttpServletRequest req, HttpServletResponse resp)
 2         throws ServletException, IOException {
 3 
 4         String method = req.getMethod();
 5 
 6         if (method.equals(METHOD_GET)) {
 7             long lastModified = getLastModified(req);
 8             if (lastModified == -1) {
 9                 // servlet doesn't support if-modified-since, no reason
10                 // to go through further expensive logic
11                 doGet(req, resp);
12             } else {
13                 long ifModifiedSince;
14                 try {
15                     ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
16                 } catch (IllegalArgumentException iae) {
17                     // Invalid date header - proceed as if none was set
18                     ifModifiedSince = -1;
19                 }
20                 if (ifModifiedSince < (lastModified / 1000 * 1000)) {
21                     // If the servlet mod time is later, call doGet()
22                     // Round down to the nearest second for a proper compare
23                     // A ifModifiedSince of -1 will always be less
24                     maybeSetLastModified(resp, lastModified);
25                     doGet(req, resp);
26                 } else {
27                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
28                 }
29             }
30 
31         } else if (method.equals(METHOD_HEAD)) {
32             long lastModified = getLastModified(req);
33             maybeSetLastModified(resp, lastModified);
34             doHead(req, resp);
35 
36         } else if (method.equals(METHOD_POST)) {
37             doPost(req, resp);
38 
39         } else if (method.equals(METHOD_PUT)) {
40             doPut(req, resp);
41 
42         } else if (method.equals(METHOD_DELETE)) {
43             doDelete(req, resp);
44 
45         } else if (method.equals(METHOD_OPTIONS)) {
46             doOptions(req,resp);
47 
48         } else if (method.equals(METHOD_TRACE)) {
49             doTrace(req,resp);
50 
51         } else {
52             //
53             // Note that this means NO servlet supports whatever
54             // method was requested, anywhere on this server.
55             //
56 
57             String errMsg = lStrings.getString("http.method_not_implemented");
58             Object[] errArgs = new Object[1];
59             errArgs[0] = method;
60             errMsg = MessageFormat.format(errMsg, errArgs);
61 
62             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
63         }
64     }

  而後在跳到 FrameworkServlet 的 doGet 方法中,doGet方法中用到 processRequest 方法,再看看上面對 patch 進行單獨處理,可見是把將不一樣類型的請求合併到了processRequest 這個方法中來處理。

1     @Override
2     protected final void doGet(HttpServletRequest request, HttpServletResponse response)
3             throws ServletException, IOException {
4 
5         processRequest(request, response);
6     }

  processRequest 方法中作了三件事:

  • 調用了 doService 模板方法具體處理請求。

  • 將當前請求的 LocaleContext 和 ServletRequestAttributes 在處理請求前設置到了 LocaleContextHolder 和 RequestContextHolder,並在請求處理完成後恢復

  • 請求處理完後發佈了 ServletRequestHandledEvent 消息

 1     protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
 2             throws ServletException, IOException {
 3 
 4         long startTime = System.currentTimeMillis();
 5         Throwable failureCause = null;
 6 
 7         LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
 8         LocaleContext localeContext = buildLocaleContext(request);
 9 
10         RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
11         ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
12 
13         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
14         asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
15 
16         initContextHolders(request, localeContext, requestAttributes);
17 
18         try {
19             doService(request, response);
20         }
21         catch (ServletException | IOException ex) {
22             failureCause = ex;
23             throw ex;
24         }
25         catch (Throwable ex) {
26             failureCause = ex;
27             throw new NestedServletException("Request processing failed", ex);
28         }
29 
30         finally {
31             resetContextHolders(request, previousLocaleContext, previousAttributes);
32             if (requestAttributes != null) {
33                 requestAttributes.requestCompleted();
34             }
35             logResult(request, response, failureCause, asyncManager);
36             publishRequestHandledEvent(request, response, startTime, failureCause);
37         }
38     }

  上面這段代碼調用了 doService 的模板方法,接着走到了 DispatcherServlet 類中的 doService方法。

 1     @Override
 2     protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
 3         logRequest(request);
 4 
 5         // Keep a snapshot of the request attributes in case of an include,
 6         // to be able to restore the original attributes after the include.
 7         Map<String, Object> attributesSnapshot = null;
 8         if (WebUtils.isIncludeRequest(request)) {
 9             attributesSnapshot = new HashMap<>();
10             Enumeration<?> attrNames = request.getAttributeNames();
11             while (attrNames.hasMoreElements()) {
12                 String attrName = (String) attrNames.nextElement();
13                 if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
14                     attributesSnapshot.put(attrName, request.getAttribute(attrName));
15                 }
16             }
17         }
18 
19         // Make framework objects available to handlers and view objects.
20         request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
21         request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
22         request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
23         request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
24 
25         if (this.flashMapManager != null) {
26             FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
27             if (inputFlashMap != null) {
28                 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
29             }
30             request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
31             request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
32         }
33 
34         try {
35             doDispatch(request, response);
36         }
37         finally {
38             if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
39                 // Restore the original attribute snapshot, in case of an include.
40                 if (attributesSnapshot != null) {
41                     restoreAttributesAfterInclude(request, attributesSnapshot);
42                 }
43             }
44         }
45     }

  而後進行請求的分發 DispatcherServlet#doDispatch

 1     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2         HttpServletRequest processedRequest = request;
 3         HandlerExecutionChain mappedHandler = null;
 4         boolean multipartRequestParsed = false;
 5 
 6         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 7 
 8         try {
 9             ModelAndView mv = null;
10             Exception dispatchException = null;
11 
12             try {
13                 processedRequest = checkMultipart(request);
14                 multipartRequestParsed = (processedRequest != request);
15 
16                 // 肯定當前請求的handler
17                 mappedHandler = getHandler(processedRequest);
18                 if (mappedHandler == null) {
19                     noHandlerFound(processedRequest, response);  // 沒有找到則執行此方法 20                     return;
21                 }
22 
23                 // 肯定當前請求的處理程序適配器
24                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
25 
26                 // Process last-modified header, if supported by the handler.
27                 String method = request.getMethod();
28                 boolean isGet = "GET".equals(method);
29                 if (isGet || "HEAD".equals(method)) {
30                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
31                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
32                         return;
33                     }
34                 }
35 
36                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
37                     return;
38                 }
39 
40                 // 調用對應handler方法
41                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
42 
43                 if (asyncManager.isConcurrentHandlingStarted()) {
44                     return;
45                 }
46 
47                 applyDefaultViewName(processedRequest, mv);
48                 mappedHandler.applyPostHandle(processedRequest, response, mv);
49             }
50             catch (Exception ex) {
51                 dispatchException = ex;
52             }
53             catch (Throwable err) {
54                 // As of 4.3, we're processing Errors thrown from handler methods as well,
55                 // making them available for @ExceptionHandler methods and other scenarios.
56                 dispatchException = new NestedServletException("Handler dispatch failed", err);
57             }
58             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
59         }
60         catch (Exception ex) {
61             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
62         }
63         catch (Throwable err) {
64             triggerAfterCompletion(processedRequest, response, mappedHandler,
65                     new NestedServletException("Handler processing failed", err));
66         }
67         finally {
68             if (asyncManager.isConcurrentHandlingStarted()) {
69                 // Instead of postHandle and afterCompletion
70                 if (mappedHandler != null) {
71                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
72                 }
73             }
74             else {
75                 // Clean up any resources used by a multipart request.
76                 if (multipartRequestParsed) {
77                     cleanupMultipart(processedRequest);
78                 }
79             }
80         }
81     }

  看看 getHandler 方法,遍歷全部配置的 HandlerMapping 類,能夠看出是支持用戶配置多個 HandlerMapping 類的,

 1     @Nullable
 2     protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 3         if (this.handlerMappings != null) {
 4             for (HandlerMapping mapping : this.handlerMappings) {
 5                 HandlerExecutionChain handler = mapping.getHandler(request);
 6                 if (handler != null) {
 7                     return handler;
 8                 }
 9             }
10         }
11         return null;
12     }

  若是沒有找到對應的 HandlerExecutionChain 對象,則會執行 noHandlerFound 方法返回異常。

 1     protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2         if (pageNotFoundLogger.isWarnEnabled()) {
 3             pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
 4         }
 5         if (this.throwExceptionIfNoHandlerFound) {
 6             throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
 7                     new ServletServerHttpRequest(request).getHeaders());
 8         }
 9         else {
10             response.sendError(HttpServletResponse.SC_NOT_FOUND);  // 404錯誤 11         }
12     }

  返回到 DispatcherServlet#doDispatch,找到了對應的 handler 後,就去尋找對應的 HandlerAdapter。與 HandlerMapping 相似,查找可以處理具體 Handler 的 HandlerAdapter 時一樣會遍歷全部配置了的 HandlerAdapter,沒有找到對應的 adapter 則會拋出異常。

 1     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
 2         if (this.handlerAdapters != null) {
 3             for (HandlerAdapter adapter : this.handlerAdapters) {
 4                 if (adapter.supports(handler)) {
 5                     return adapter;
 6                 }
 7             }
 8         }
 9         throw new ServletException("No adapter for handler [" + handler +
10                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
11     }

  前沒有什麼問題發生,則按照順序依次調用 HandlerInterceptor的 preHandle 方法,但當任一 HandlerInterceptor 的 preHandle方法返回了 false 就再也不繼續執行其餘HandlerInterceptor 的 preHandle方法,而是直接跳轉執行 triggerAfterCompletion 方法。

 1     boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2         HandlerInterceptor[] interceptors = getInterceptors();
 3         if (!ObjectUtils.isEmpty(interceptors)) {
 4             for (int i = 0; i < interceptors.length; i++) {
 5                 HandlerInterceptor interceptor = interceptors[i];
 6                 if (!interceptor.preHandle(request, response, this.handler)) {
 7                     triggerAfterCompletion(request, response, null);
 8                     return false;
 9                 }
10                 this.interceptorIndex = i;
11             }
12         }
13         return true;
14     }

  而後執行 mapperHandler 的 applyPostHandle 方法,應用註冊攔截器的 postHandle 方法。

    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = interceptors.length - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }
    }

  最後執行 processDispatchResult 方法

 1     /**
 2      * Handle the result of handler selection and handler invocation, which is
 3      * either a ModelAndView or an Exception to be resolved to a ModelAndView.
 4      */
 5     private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
 6             HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
 7 
 8         boolean errorView = false;
 9         //判斷 HandlerMapping、HandlerAdapter 處理時的異常是否有異常,有異常則進行異常處理
10         if (exception != null) {
11             //上述兩個組件處理時的異常不爲空
12             //若是爲 ModelAndViewDefiningException 異常,則獲取一個異常視圖
13             if (exception instanceof ModelAndViewDefiningException) {
14                 logger.debug("ModelAndViewDefiningException encountered", exception);
15                 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
16             }
17             //若是不爲 ModelAndViewDefiningException 異常,進行異常視圖的獲取
18             else {
19                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
20                 mv = processHandlerException(request, response, handler, exception);
21                 errorView = (mv != null);
22             }
23         }
24 
25         // Did the handler return a view to render?
26         //判斷 mv 是否爲空,不論是正常的 ModelAndView 仍是異常的 ModelAndView,只要存在 mv 就進行視圖渲染
27         if (mv != null && !mv.wasCleared()) {
28             render(mv, request, response); // 交給rander渲染 29             if (errorView) {
30                 WebUtils.clearErrorRequestAttributes(request);
31             }
32         }
33         //不然記錄無視圖
34         else {
35             if (logger.isDebugEnabled()) {
36                 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
37                         "': assuming HandlerAdapter completed request handling");
38             }
39         }
40 
41         if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
42             // Concurrent handling started during a forward
43             return;
44         }
45         //執行相關HandlerInterceptor的afterCompletion 方法
46         if (mappedHandler != null) {
47             mappedHandler.triggerAfterCompletion(request, response, null);
48         }
49     }

  到這裏以後則按着棧幀一步步彈出,DispatcherServlet 處理完 Http 請求的流程。

參考:

Spring MVC源碼初探(一) FrameworkServlet

SpringMVC源碼系列:九大組件小記

servlet之模板方法和多線程

相關文章
相關標籤/搜索