小编从事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: testServlet1和 testServlet2,这两个也是有先后执行顺序的。
现在将四者的大致编码展示如下:
2.Filter:
3.servlet
通过分析web.xml的定义我们知道:定义的先后顺序:
上下文变量param --> TestListener2 --> TestListener1 --> testFilter1 --> testFilter2 --> testServlet2 --> testServlet1
那么运行的结构也是按照这种先后关系来的吗?我们先来启动项目看运行结果:
上下文变量的值最先放到ServletContext容器中,因为在监听器中已经能取到上下文变量的值。
监听器的执行先后顺序是按照web.xml里面定义的先后顺序来执行的,
filter是无序随机执行的,
servlet是按照<load-on-startup>的等级定义来区分先后顺序的,值越小优先级越大,值大于等于0.
再来看看项目销毁时的执行顺序:
我们看到,最后执行的 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销毁。。。 <--
应用上下文:即程序上下文,也就是整个程序,可以把它想做一个容器,里面可以放各种各样的变量,这个容器能被整个程序共享,我们知道一个用户能拥有多个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:
我们先来看一个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: 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);
}
}
上下文变量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销毁。。。 <--
filter1销毁。。。 <--
filter2销毁。。。 <--
listener test1 销毁。。。 <--
listener test2销毁。。。 <--
好了,就写这么多了,原创不易,若要转载请注明出处!
可以下载源码运行!!!!