1. 程式人生 > >web項目啟動流程探索

web項目啟動流程探索

也有 當我 存在 初始化 char ext.get 獲取 所有 jdk

  在web項目的啟動過程中,我們希望知道它的一般流程是什麽,這樣我們就可以在各個流程中加入相應的功能,或者對於我們排錯也有幫助。

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

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

<?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的主要配置項有如下幾種。

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

  b.listener:listener就是監聽器,他會監聽一些變化(比如servlet的初始化),然後就可以觸發監聽器的代碼了。

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

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

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

技術分享

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

技術分享

  我們可以看到,項目的啟動順序首先是context-param,接著是listener,在接下來是filter,最後才是servlet。

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

  答案是否定的,即便是吧關於servlet的配置放在最前面,其加載順序還是會在最後。但是需要註意的是,在同一類型的配置項中,其在web.xml的順序會影響其啟動的順序,比如有兩個filter,filter1在配置文件中先於filter2,則filter1先於filter2被加載實例化。

  ps2: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);

  ps3: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容器生成的。

  ps4:DispatcherServlet是什麽?有什麽用。

  簡單來說,它就是一個servlet,但是它是一個特殊的servlet,是整個spring mvc框架的核心,他是一個前端servlet,spring mvc經過前端servlet來接受所有的請求,然後再講具體工作派發給其他的的servlet來具體實現。

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

  ps5:由於初始化DispatcherServlet伴隨著啟動spring mvc容器(即上面所說的web層容器),所以需要較長的時間,所以我們希望在項目啟動的時候就進行初始化的操作。這也是我們將load-on-startup項設為1的原因。因為這個屬性設為正數的表示在項目啟動的時候就初始化,數字越小表明越早初始化。如果我們將其設為負數的話。那麽在項目啟動的時候,將不會啟動spring mvc的容器,而是當我們第一次訪問某個controller所對應的action的時候才來加載啟動容器,這將會造成較長時間的等待,所以我們一般將load-on-startup設為1.

web項目啟動流程探索