应用上下文:即程序上下文,也就是整个程序,可以把它想做一个容器,里面可以放各种各样的变量,这个容器能被整个程序共享,我们知道一个用户能拥有多个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销毁。。。 <--
好了,就写这么多了,原创不易,若要转载请注明出处!
可以下载源码运行!!!!