web項目啓動流程探索

  在web項目的啓動過程當中,咱們但願知道它的通常流程是什麼,這樣咱們就能夠在各個流程中加入相應的功能,或者對於咱們排錯也有幫助。前端

  咱們知道,當咱們啓動tomcat容器之後,容器首先初始化一些必要的組件,加載項目所引用到的jar包(分別從jdk,tomcat,還有web-inf中的lib目錄下),而後接下來的一步就是去讀取web項目的web.xml配置文件。因此web項目裏面必需要有web.xml配置文件。java

 

  咱們來看一份標準的web.xml配置文件,這是我從個人項目中抽取出來的。web

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">
    <!-- spring上下文 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:ApplicationContext.xml,
        </param-value>
    </context-param>
    <!-- 加載log4j配置文件 -->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j.properties</param-value>
    </context-param>
    <context-param>
        <param-name>webAppRootKey</param-name>
        <param-value>www.warrior.com</param-value>
    </context-param>
    <!-- 監聽器 -->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener
        </listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <!-- 字符編碼過濾器 -->
    <filter>
        <filter-name>encodingFilter</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>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 初始化filter -->
    <filter>
        <filter-name>startFilter</filter-name>
        <filter-class>com.xdx.filter.StartFilter</filter-class>
    </filter>
    <!-- session過濾器 -->
    <filter>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 如下配置是spring mvc -->
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:ApplicationContext-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <error-page>
        <error-code>404</error-code>
        <location>/404.jsp</location>
    </error-page>
    <session-config>
        <session-timeout>600</session-timeout>
    </session-config>
</web-app>

   

  能夠看出web.xml的主要配置項有以下幾種。spring

  a.context-param:上下文參數,經過鍵值對的方式配置一些上下文信息,如contextConfigLocation,咱們給他配置爲classpath:ApplicationContext.xml,說明去classpath:ApplicationContext.xml這個地方去尋找spring的主配置文件。tomcat

  b.listener:listener就是監聽器,他會監聽一些變化(好比servlet的初始化),而後就能夠觸發監聽器的代碼了。session

  c.filter:過濾器,顧名思義,就是對請求進行過濾,filter也會在項目啓動的時候被實例化。通常一個filter要對應filter-mapping,用於篩選所要執行過濾器中代碼的url路徑。若是一個filter沒有filter-mapping,那它存在的意義就不大,它在web.xml存在的目的純粹就是爲了在項目啓動的時候被實例化,從而執行其內部的代碼。上述配置文件中的startFilter就是這個做用。mvc

  d.servlet,servlet的配置與filter相似,就是對請求進行攔截,不一樣的請求分配到不一樣的servlet類進行處理。app

  爲了觀察項目中各組件的啓動順序,我在相關的dao,entity類,service類,controllers類均加上了static代碼塊和無參的構造函數。static代碼塊是在類加載的時候運行,構造函數在類實例化時候運行,以下所示。框架

 

  

   運行項目。看看控制檯打印出來的日誌。jsp

  

  咱們能夠看到,項目的啓動順序首先是context-param,接着是listener,在接下來是filter,最後纔是servlet。

  

  問題1:改換context-param,listener,filter,servlet配置語句在web.xml中的順序,其啓動順序是否會變呢?

  答案是否認的,即使是吧關於servlet的配置放在最前面,其加載順序仍是會在最後。可是須要注意的是,在同一類型的配置項中,其在web.xml的順序會影響其啓動的順序,好比有兩個filter,filter1在配置文件中先於filter2,則filter1先於filter2被加載實例化。

  

  問題2:org.springframework.web.context.ContextLoaderListener的做用。

  ContextLoaderListener這個監聽器繼承自ContextLoader而且實現了ServletContextListener,他的主要做用是去尋找並讀取spring主配置文件ApplicationContext.xml(也就是context-param中所定義的contextConfigLocation),而後啓動WebApplicationContext,也可叫作web應用上下文,而且最重要的是,它將WebApplicationContext注入到servletContext容器中(做爲servletContext的一個attribute,屬性),而且在WebApplicationContext中保留了一個servletContext的引用。因此咱們能夠經過

   WebApplicationContext獲得servletContext,也能夠經過servletContext獲取到WebApplicationContext

  經過WebApplicationContext獲得servletContext:

  WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();

    ServletContext servletContext = webApplicationContext.getServletContext();

  經過servletContext獲取WebApplicationContext:

  ServletContext servletContext = event.getServletContext();

  ApplicationContext application = WebApplicationContextUtils .getWebApplicationContext(servletContext);

 

  問題3:webApplicationContext和servletContext是誰先存在呢?

  固然是servletcontext,ServletContext是web容器(tomcat等)爲web項目提供的一個全局上下文,一個web項目中只有一個。它實際上是後面生成的WebApplicationContext容器的一個宿主。

  

  因此:簡單來講:web項目啓動通過以下步驟。

  1.項目啓動,加載依賴的jar包。

  2.web容器(tomcat)先提供一個全局上下文ServletContext.

  3.web容器去讀取web.xml文件,而且運行ContextLoaderListener監聽器,該監聽器由於實現了ServletContextListener接口,因此當發現容器生成了一個ServletContext實例的時候,便會執行ServletContextListener接口的初始化方法,在該初始化方法中根據contextConfigLocation指定的位置去讀取spring的主要配置文件,而後生成web應用上下文WebApplicationContext,而且將其做爲一個屬性注入到ServletContext中。

  4.初始化WebApplicationContext之後,啓動了「業務層」的spring容器,並開始加載病初始化applicationContext配置文件中所掃描的類。

  5.而後就是初始化filter,最後初始化servlet。

  因此說做爲web項目,WebApplicationContext的生成必需要在web容器存在的狀況下才能實現,由於他須要ServletContext,而ServletContext是web容器生成的。

  

  問題4:DispatcherServlet是什麼?有什麼用。

  簡單來講,它就是一個servlet,可是它是一個特殊的servlet,是整個spring mvc框架的核心,他是一個前端servlet,spring mvc通過前端servlet來接受全部的請求,而後再講具體工做派發給其餘的的servlet來具體實現。

同時,再servlet的配置文件中,咱們看到名爲SpringMvc的讀取了contextConfigLocation所定義的配置文件(classpath:ApplicationContext-mvc.xml),啓動了web層的spring容器,在這個容器裏,咱們初始化了全部的controller類。如控制檯打印的日誌所示。

  

  問題5:因爲初始化DispatcherServlet伴隨着啓動spring mvc容器(即上面所說的web層容器),因此須要較長的時間,因此咱們但願在項目啓動的時候就進行初始化的操做。這也是咱們將load-on-startup項設爲1的緣由。由於這個屬性設爲正數的表示在項目啓動的時候就初始化,數字越小代表越早初始化。若是咱們將其設爲負數的話。那麼在項目啓動的時候,將不會啓動spring mvc的容器,而是當咱們第一次訪問某個controller所對應的action的時候纔來加載啓動容器,這將會形成較長時間的等待,因此咱們通常將load-on-startup設爲1.

相關文章
相關標籤/搜索