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.

相关文章
相关标签/搜索