ServletContext,Listener,Filter和自启动Servlet的执行顺序

时间:2021-09-25 18:24:19
小编从事j2ee开发快两年,平时的项目中上下文变量和监听器,过滤器和自启动servlet都在用,可是一直没整明白这四者的执行顺序,以及何为上下文,如果也有这样疑问的童孩有缘看到这篇文章,那么恭喜你,找对地方了本文是小编个人的学习笔记,有不妥或错误支持,欢迎指正。
        
        应用上下文:即程序上下文,也就是整个程序,可以把它想做一个容器,里面可以放各种各样的变量,这个容器能被整个程序共享,我们知道一个用户能拥有多个request,一个用户只能拥有一个session,那么这个上下文就是所有用户有且只能拥有一个,类似于spring的容器的概念。上下文对象即ServletContext,此对象就是应用上下文了,好比spring中的ApplicationContext,我们在应用spring框架进行依赖注入的时候,依赖注入的方式一般分为两种,xml文件配置和注解形式,起始除了这两种方式外,还可以通过先从ServletContext上下文中将ApplicationContext取到,然后直接通过ApplicationContext.getBean(name)获取你想要的对象,形象的比喻:容器是一口大锅,对象是锅里的饺子,你想吃哪个饺子了直接用勺子去舀就行了,ServletContext就是这么个道理。
具体定义参见: http://blog.csdn.net/lvzhiyuan/article/details/4664624
        
        listener,filter的定义和讲解请度娘,在这里就不做赘述了,此文着重讲解四者的执行顺序。
        自启动servlet:自启动servlet和普通servlet在创建和原理上基本一样,只是在web.xml里面配置稍有不同,写过servlet的童孩应该都知道一个servlet要被触发,只有通过请求映射相应url去访问,也就是说servlet的调用时被动的,需要请求触发。自启动servlet的触发则是在项目启动的时候,初始化方法就执行了,看一下web.xml
        <servlet>
<servlet-name>testServlet1</servlet-name>
<servlet-class>com.servlet.ServletTest1</servlet-class>
<init-param>
<param-name>testServlet1</param-name>
<param-value>i am testServlet1's param</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>


没错,就是多了<load-on-startup>标签,这个servlet就成了自启动servlet,我们可以应用自启动servlet完成一些项目初始化工作,比如在项目启动的时候就将数据库里面一些重要的信息先=加载进内存以作备用,不至于要用的时候才去查数据库降低效率。若一个servlet被配置成了自启动servlet,那么在servleyt类里就要重写父类的方法init(),顾名思义就是初始化。

         我们先来看一个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" 
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
id="WebApp_ID" 
version="3.0">
<!--上下文变量-->
<context-param>
<param-name>param</param-name>
<param-value>111</param-value>
</context-param>

         <!--监听器--> 
<listener>
<listener-class>com.listener.TestListener2</listener-class>
</listener>
<listener>
<listener-class>com.listener.TestListener1</listener-class>
</listener>

        <!--过滤器--> 
<filter>
<filter-name>testFilter1</filter-name>
<filter-class>com.filter.TestFilter1</filter-class>
<init-param>
<param-name>filter1Params</param-name>
<param-value>i am filter1's params</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>testFilter1</filter-name>
<url-pattern>/test1/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>testFilter2</filter-name>
<filter-class>com.filter.TestFilter2</filter-class>
<init-param>
<param-name>filter2Params</param-name>
<param-value>i am filter2's params</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>testFilter2</filter-name>
<url-pattern>/test2/*</url-pattern>
</filter-mapping>

        <!--自启动servlet--> 
<servlet>
<servlet-name>testServlet2</servlet-name>
<servlet-class>com.servlet.ServletTest2</servlet-class>
<init-param>
<param-name>testServlet2</param-name>
<param-value>i am testServlet2's param</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>testServlet2</servlet-name>
<url-pattern>/testServlet2</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>testServlet1</servlet-name>
<servlet-class>com.servlet.ServletTest1</servlet-class>
<init-param>
<param-name>testServlet1</param-name>
<param-value>i am testServlet1's param</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>testServlet1</servlet-name>
<url-pattern>/testServlet1</url-pattern>
</servlet-mapping>
</web-app> 


 
小编用四种颜色分别标记出了上下文变量,监听器,过滤器和自启动Servlet
在这里小编着重申明一点:执行顺序分两侧意思:四者的先后执行顺序,和多个同一类型构件的执行顺序,比如上文中有两个自启动servlet: testServlet1和 testServlet2,这两个也是有先后执行顺序的。

 

现在将四者的大致编码展示如下:

1.Listener

public class TestListener1 implements ServletContextListener {
private static Logger logger = Logger.getLogger(TestListener1.class);
public void contextDestroyed(ServletContextEvent event) {
logger.debug("listener test1 销毁。。。");
}
public void contextInitialized(ServletContextEvent event) {
logger.debug("listener test1 初始化。。。");
//获取上下文的初始化参数
String param = event.getServletContext().getInitParameter("param");
logger.debug("listener : context-param : "+param);
}
}



2.Filter:
public class TestFilter1 implements Filter {
private static Logger logger = Logger.getLogger(TestFilter1.class);
public void destroy() {
logger.debug("filter1销毁。。。");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
logger.debug("filter1 doFilter之前。。。。");
chain.doFilter(request, response);
logger.debug("filter1 doFilter之后。。。。");
}
public void init(FilterConfig config) throws ServletException {
String params = config.getInitParameter("filter1Params");
logger.debug("filter1 params="+params);
//获取上下文的初始化参数
String param = config.getServletContext().getInitParameter("param");
String hello = config.getServletContext().getInitParameter("hello");
logger.debug("testFilter1 : context-param : "+param+" hello="+hello);
}
} 



3.servlet
public class TestFilter1 implements Filter {
private static Logger logger = Logger.getLogger(TestFilter1.class);
public void destroy() {
logger.debug("filter1销毁。。。");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
logger.debug("filter1 doFilter之前。。。。");
chain.doFilter(request, response);
logger.debug("filter1 doFilter之后。。。。");
}
public void init(FilterConfig config) throws ServletException {
String params = config.getInitParameter("filter1Params");
logger.debug("filter1 params="+params);
//获取上下文的初始化参数
String param = config.getServletContext().getInitParameter("param");
logger.debug("testFilter1 : context-param : "+param);
}

}



通过分析web.xml的定义我们知道:定义的先后顺序:
上下文变量param -->  TestListener2 -->  TestListener1 -->  testFilter1 -->  testFilter2 -->  testServlet2 -->  testServlet1
那么运行的结构也是按照这种先后关系来的吗?我们先来启动项目看运行结果:
 2016-12-15 17:15:22,424 DEBUG [TestListener2.java:17] : listener test2 初始化。。。
2016-12-15 17:15:22,426 DEBUG [TestListener1.java:17] : listener test1 初始化。。。
2016-12-15 17:15:22,427 DEBUG [TestListener1.java:21] : listener : context-param : 111
2016-12-15 17:15:22,427 DEBUG [TestFilter1.java:33] : filter1 params=i am filter1's params
2016-12-15 17:15:22,428 DEBUG [TestFilter1.java:36] : testFilter1 : context-param : 111
2016-12-15 17:15:22,428 DEBUG [TestFilter2.java:33] : filter2 params=i am filter2's params
2016-12-15 17:15:22,429 DEBUG [ServletTest1.java:56] : ServletTest1初始化。。。
2016-12-15 17:15:22,429 DEBUG [ServletTest1.java:47] : ServletTest1初始化。。。i am testServlet1's param
2016-12-15 17:15:22,429 DEBUG [ServletTest1.java:50] : testServlet1 : context-param : 111
2016-12-15 17:15:22,429 DEBUG [ServletTest2.java:53] : ServletTest2初始化。。。
2016-12-15 17:15:22,430 DEBUG [ServletTest2.java:47] : ServletTest2初始化。。。i am testServlet2's param



上下文变量的值最先放到ServletContext容器中,因为在监听器中已经能取到上下文变量的值。
监听器的执行先后顺序是按照web.xml里面定义的先后顺序来执行的,
filter是无序随机执行的,
servlet是按照<load-on-startup>的等级定义来区分先后顺序的,值越小优先级越大,值大于等于0. 

再来看看项目销毁时的执行顺序:
2016-12-15 17:20:04,455 DEBUG [ServletTest2.java:40] : ServletTest2销毁。。。
2016-12-15 17:20:04,455 DEBUG [ServletTest1.java:40] : ServletTest1销毁。。。
2016-12-15 17:20:04,456 DEBUG [TestFilter1.java:19] : filter1销毁。。。
2016-12-15 17:20:04,456 DEBUG [TestFilter2.java:19] : filter2销毁。。。
2016-12-15 17:20:04,456 DEBUG [TestListener1.java:13] : listener test1 销毁。。。
2016-12-15 17:20:04,456 DEBUG [TestListener2.java:13] : listener test2销毁。。。



我们看到,最后执行的 ServletTest2却最先被销毁,说明销毁顺序跟启动执行顺序是刚好相反的,也说明了j2ee规范是按照环绕式责任链模式来设计的:
上下文变量param
        -->  
listener test2 初始化。。。
            -->  listener test1 初始化。。。
                -->   filter1 params=i am filter1's params
                    -->  filter2 params=i am filter2's params
                        -->  ServletTest1初始化。。。
                            -->  ServletTest2初始化。。。
                                            ======>  应用服务
                             
ServletTest2销毁。。。  <--
                        ServletTest1销毁。。。  <--
                    filter1销毁。。。 <--
                filter2销毁。。。 <--
            listener test1 销毁。。。 <--
        listener test2销毁。。。 <--

好了,就写这么多了,原创不易,若要转载请注明出处! 

可以下载源码运行!!!!