Web容器初始化過程

1、SpringMVC啓動過程

Spring的MVC是基於Servlet功能實現的,每一個web工程中都有一個web.xml文件,web容器在啓動的時候會加載這個配置文件,當一個web應用加載到web容器中後,在web應用開始響應客戶端的請求以前,要按照順序執行下面幾個步驟:html

一、實例化部署描述符中的<listener>元素標識的時間監聽器的實例;web

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

二、在實現了ServletContextListener接口的監聽器實例中,調用contextInitialized()方法。ContextLoaderListener實現了ServletContextListener接口,ServletContextListener做爲Servlet的監聽者,會在ServletContext建立、銷燬過程當中監聽ServletContextEvent事件,而後進行響應的處理。換句話說,就是在啓動web容器時,ContextLoaderListener類會自動裝配ApplicationContext的配置信息。spring

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }
  // 這裏調用其父類ContextLoader的initWebApplicationContext進行初始化工做   
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
  
    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

  ContextLoaderListener類繼承了ContextLoader,後者是實際的ServletContextListener接口實現者,這裏用到了代理模式,利用ContextLoader類建立Spring application context,並將其註冊到ServletContext中去。ContextLoaderListener就是調用ContextLoader類的initWebApplicationContext()方法和closeWebApplicationContext()方法來實現ServletContext的建立和銷燬工做的。架構

  

   其中,initWebApplicationContext()方法主要完成了兩個部分的工做:一是查看是否已經指定父容器,若是不存在則設置其父容器。二是將Spring WebApplicationContext放置到線程Map中。app

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
        } else {
            Log logger = LogFactory.getLog(ContextLoader.class);
            servletContext.log("Initializing Spring root WebApplicationContext");
            if (logger.isInfoEnabled()) {
                logger.info("Root WebApplicationContext: initialization started");
            }

            long startTime = System.currentTimeMillis();

            try {
                if (this.context == null) {
                    this.context = this.createWebApplicationContext(servletContext);
                }

                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
                    if (!cwac.isActive()) {
              //查看是否指定父容器
                        if (cwac.getParent() == null) {
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }

                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }

                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                //將WebApplicationContext放置到線程Map中
                if (ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;
                } else if (ccl != null) {
                    currentContextPerThread.put(ccl, this.context);
                }

                if (logger.isDebugEnabled()) {
                    logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                }

                if (logger.isInfoEnabled()) {
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                }

                return this.context;
            } catch (RuntimeException var8) {
                logger.error("Context initialization failed", var8);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);
                throw var8;
            } catch (Error var9) {
                logger.error("Context initialization failed", var9);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);
                throw var9;
            }
        }
    }    

在初始化方法執行後,將WebApplicationContext存到了兩個地方,分別是ServletContext和currentContextPerThread中,咱們能夠經過這兩個地方來獲取WebApplicationContext:1.從servletContext中去拿;2.從當前的線程Map中去拿。webapp

三、實例化部署符中<filter>元素標識的過濾器實例,並調用每一個過濾器實例的init()方法。async

四、實例化部署符中<servlet>元素標識的Servlet實例,按照該元素包含的load-on-startup順序,調用每一個Servlet實例的init()方法。函數

  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

在SpringMVC的架構中,DispatcherServlet主要負責客戶端請求的分發,起到控制器的做用。DispatcherServlet是實現Servlet接口的實現類,其結構以下圖所示。Servlet的生命週期由Servlet的容器來控制,主要分爲初始化、運行和銷燬三個階段,分別對應三個方法:init()、service()和destory()。ui

  在DispatcherServlet的父類HTTPServletBean中,重寫了init()方法,主要實現將當前的Servlet類實例轉換爲BeanWrapper類型實例,以便使用Spring中提供的注入功能進行相應屬性的注入。this

  在DispatcherServlet的父類FameworkServelet中,重寫了service()方法,對於不一樣的方法,Spring統一將程序引導至processRequest(request,response)中,processRequest方法再對處理請求進行了一些準備工做後,又將細節實現轉移到了doService方法中。

  protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = request.getMethod();
        if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
            this.processRequest(request, response);
        } else {
            super.service(request, response);
        }

   }
  protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = this.buildLocaleContext(request);
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
        this.initContextHolders(request, localeContext, requestAttributes);
        try {
            //調用DispatcherServlet的doService()方法
            this.doService(request, response);
        } catch (ServletException var17) {
            failureCause = var17;
            throw var17;
        } catch (IOException var18) {
            failureCause = var18;
            throw var18;
        } catch (Throwable var19) {
            failureCause = var19;
            throw new NestedServletException("Request processing failed", var19);
        } finally {
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }
            if (this.logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", (Throwable)failureCause);
                } else if (asyncManager.isConcurrentHandlingStarted()) {
                    this.logger.debug("Leaving response open for concurrent processing");
                } else {
                    this.logger.debug("Successfully completed request");
                }
            }
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }
    }        
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }

        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();
            label108:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label108;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        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);

        try {
           // 最終的請求處理函數
            this.doDispatch(request, response);
        } finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

  通過層層的準備工做,最終在doDispatch()方法中實現了完整的請求處理過程:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                //根據request信息尋找對應的處理器
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                //根據處理器尋找對應的處理器適配器
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (this.logger.isDebugEnabled()) {
                            this.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;
                    }
                   //返回視圖
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(request, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var19) {
                    dispatchException = var19;
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            } catch (Exception var20) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var20);
            } catch (Error var21) {
                this.triggerAfterCompletionWithError(processedRequest, response, mappedHandler, var21);
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }    

2、ServletContext、ApplicationContext和WebApplicationContext區別?

  一、ServletContext:是一個Web應用的上下文,是一個全局信息的存儲空間,表明當前的web應用。Servlet容器(如Tomcat)在啓動一個Webapp時,會爲它建立一個ServletContext對象,即servlet上下文環境。每一個webapp都有惟一的ServletContext對象。同一個webapp的全部servlet對象共享一個ServeltContext,servlet對象能夠經過ServletContext來訪問容器中的各類資源。ServletContext在web應用啓動時建立,在web應用服務關閉時釋放。

   二、ApplicationContext:它是創建在BeanFactory基礎之上的,除了包含BeanFactory的全部功能以外,在國際化支持、資源訪問、事件傳播等方面進行了良好的支持。其體系結構以下圖。

  ApplicationContext接口有三個經常使用的實現類,以下:

類名稱 功能描述
ClassPathXmlApplicationContext
從類路徑ClassPath中尋找指定的XML配置文件,找到並裝載
完成ApplicationContext的實例化工做
FileSystemXmlApplicationContext
從指定的文件系統路徑中尋找指定的XML配置文件,找到並裝載
完成ApplicationContext的實例化工做
XmlWebApplicationContext 從Web應用中尋找指定的XML配置文件,找到並裝載完成ApplicationContext的實例化工做

  與BeanFactory不一樣的是,ApplicationContext容器實例化後會自動對全部的單實例Bean進行實例化與依賴關係的裝配,使之處於待用狀態。而BeanFactory容器實例化後並不會自動實例化Bean,只有當Bean被使用時BeanFactory容器纔會對該Bean進行實例化與依賴關係的裝配。

  三、WebApplicationContext:它是專門爲web應用提供的,它容許從相對於web根目錄路徑中裝載配置文件完成初始化;從WebApplicationContext中能夠得到ServletContext的引用,同時爲了方便web應用訪問Spring應用上下文,WebApplicationContext也將做爲一個屬性放到ServletContext中,能夠經過WebApplicationContextUtils的getWebApplicationContext(ServletContext sc)方法獲取。

3、總結

  ApplicationContext是Spring的核心,Context爲上下文環境,在Web應用中,會用到WebApplicationContext,它繼承自ApplicationContext。在SpringMVC啓動過程當中,ContextLoaderListener類起着相當重要的做用。它讀取web.xml中配置的context-param中的配置文件,提早在web容器初始化前準備業務對應的Application context,將建立好的WebApplicationContext放置於ServletContext屬性中,這樣咱們只要獲得Servlet就能夠獲得WebApplicationContext對象,並利用這個對象訪問spring容器管理的bean。

4、參考資料

  一、http://www.javashuo.com/article/p-wuvvkifd-by.html

  二、spring源碼深度解析(郝佳)

相關文章
相關標籤/搜索