1. 程式人生 > >SpringMVC 啟動流程

SpringMVC 啟動流程

patch null all TE err 處理請求 tex tle any

首先看一下Web應用部署初始化過程 (Web Application Deployement),官方文檔說明:

Web Application Deployment
When a web application is deployed into a container, the following steps must be performed, in this order, before the web application begins processing client requests.
■ Instantiate an instance of each event listener identified by a <listener> element in the deployment descriptor.
■ For instantiated listener instances that implement ServletContextListener, call the contextInitialized() method.
■ Instantiate an instance of each filter identified by a <filter> element in the deployment descriptor and call each filter instance’s init() method.
■ Instantiate an instance of each servlet identified by a <servlet> element that includes a <load-on-startup> element in the order defined by the load-onstartup
element values, and call each servlet instance’s init() method.

大致說:

Web應用部署:當一個web應用被部署到一個容器(eg.tomcat),在web應用開始處理客戶端請求前,以下步驟會按順序執行:

1.初始化應用部署描述文件中每一個listener。

2.初始化ServletContextListener實現類,調用contextInitialized()方法。

3.初始化應用部署描述文件中每一個filter,並執行每一個的init()方法。

4.按照順序<load-on-startup>來初始化servlet,並執行init()方法。


大致總結:先初始化lisener,再filter,最後servlet

SpringMVC啟動過程:

常見SpringMVC配置:
<web-app>
  <display-name>Web Application</display-name>
  <!--全局變量配置-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext-*.xml</param-value>
  </context-param>
  <!--監聽器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--解決亂碼問題的filter-->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!--Restful前端控制器-->
  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

DispatchServlet使用說明:
其類圖如下:
技術分享圖片
可以明顯看出DispatchServlet類間接父類實現了Servlet接口,因此其本質上依舊是一個Servlet。DispatchServlet類設計很巧妙,上層父類
不同程度的實現了相關接口的部分方法,並留出相關方法用於子類覆蓋,將不變的部分統一實現,將變化的部分預留方法用於子類實現。

DispatchServlet類初始化過程函數調用圖:
技術分享圖片
通過類圖和相關初始化函數調用的邏輯來看,DispatchServlet的初始化過程將模板方法,其父類完成不同的統一工作,並預留出相關方法用於子類
覆蓋去完成不同的可變工作。DispatchServlet類的本質是Servlet,在web應用部署到容器後進行Servelt初始化時會調用相關的init(ServletConfig)
方法,因此,DispatchServlet類的初始化過程也由該方法開始。其中FrameworkServlet抽象類中的initServletBean()方法、initWebApplicationContext()
方法以及DispatchServlet類中的onFresh()方法。

技術分享圖片

FrameworkServlet initServletBean()方法源碼,該方法重寫了FrameworkServlet抽象類在執行,終於,initXXXContext的字眼出現了
initWebApplicationContext()方法會首先從ServletContext中獲取到由ContextLoaderListener初始化完成並放進入的根容器對象引用
(因為創建子容器必須將父容器作為參數傳遞進去),然後經過層層調用,最終在createWebApplicationContext()中完成了容器的創建工作,
該方法的主要代碼如下:

技術分享圖片
技術分享圖片

技術分享圖片
好了,到此就初始化完成

DispatchServlet處理請求流程:
/**
     * Process the actual dispatching to the handler.
     * <p>The handler will be obtained by applying the servlet‘s HandlerMappings in order.
     * The HandlerAdapter will be obtained by querying the servlet‘s installed HandlerAdapters
     * to find the first that supports the handler class.
     * <p>All HTTP methods are handled by this method. It‘s up to HandlerAdapters or handlers
     * themselves to decide which methods are acceptable.
     * @param request current HTTP request
     * @param response current HTTP response
     * @throws Exception in case of any kind of processing failure
     */
    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 || mappedHandler.getHandler() == 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 (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    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);
                }
            }
        }
    }


SpringMVC 啟動流程