一.简介
监听器:专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象
Servlet 监听器:用于监听 web 应用程序中的 ServletContext, HttpSession 和 ServletRequest 等域对象的创建
与销毁事件,以及监听这些域对象中的属性发生修改的事件。
servlet监听器分类:
1.监听域对象自身的创建和销毁的事件监听器
2.监听域对象中的属性的增加和删除的事件监听器
3.监听绑定到 HttpSession 域中的某个对象的状态的事件监听器
Servlet 规范为每种事件监听器都定义了相应的接口,开发人员编写的事件监听器程序只需实现这些接口,
web 服务器根据用户编写的事件监听器所实现的接口把它注册到相应的被监听对象上
一些Servlet监听器需要在web.xml中注册,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。
一个 web 应用程序只会为每个事件监听器创建一个对象
二.监听器接口
1.监听域对象自身的创建和销毁的事件监听器
用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象的创建和销毁事件的监听器
(1)ServletContextListener 接口
在servlet-api.jar中查看源码如下:
package javax.servlet; import java.util.EventListener; public abstract interface ServletContextListener extends EventListener { //当 ServletContext 对象被创建时调用 public abstract void contextInitialized(ServletContextEvent paramServletContextEvent); //当 ServletContext 对象被销毁时调用 public abstract void contextDestroyed(ServletContextEvent paramServletContextEvent); }
ServletContext对象在web应用被web容器加载时创建,在web应用被卸载时销毁
比较常用,可以对web应用被加载时对web应用的相关资源进行初始化工作:如创建数据库连接池,创建Spring IOC容器,读取当前Web应用的初始化参数
其中ServletContextEvent只有一个方法:getServletContext()获取ServletContext
(2)HttpSessionListener 接口
package javax.servlet.http; import java.util.EventListener; public abstract interface HttpSessionListener extends EventListener { public abstract void sessionCreated(HttpSessionEvent paramHttpSessionEvent);//Session创建时调用 public abstract void sessionDestroyed(HttpSessionEvent paramHttpSessionEvent);//Session销毁时调用 }
HttpSession对象在第一次访问web应用的JSP或Servlet,并且该Jsp或Servlet里还需要创建Session时,web容器会创建Session
这里的还需要要特别强调,例如当第一次访问index.jsp,但是index.jsp中设置了<%@ page session="false" %>session也不会创建
HttpSession销毁有以下几种情况:session失效、调用Session的invalidate方法、服务器进程被停止;注意在web容器被卸载时,session并未被销毁,
而是被持久化了,生成session.src存放在磁盘中,等web容器再次被装载时,该session又被活化了
(3)ServletRequestListener接口
package javax.servlet; import java.util.EventListener; public abstract interface ServletRequestListener extends EventListener { public abstract void requestDestroyed(ServletRequestEvent paramServletRequestEvent);//Request销毁时调用 public abstract void requestInitialized(ServletRequestEvent paramServletRequestEvent);//Request创建时调用 }
ServletRequest对象在发送请求时创建,返回响应时销毁,注意请求转发只发送了一个请求,重定向是两个请求,放在request属性中的值无法在获取;
2.域对象中属性的变更的事件监听器
监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器。
(1)ServletContextAttributeListener接口
对Servlet上下文属性的监听(增删改属性)
package javax.servlet; import java.util.EventListener; public abstract interface ServletContextAttributeListener extends EventListener { public abstract void attributeAdded(ServletContextAttributeEvent paramServletContextAttributeEvent);//添加 public abstract void attributeRemoved(ServletContextAttributeEvent paramServletContextAttributeEvent);//删除 public abstract void attributeReplaced(ServletContextAttributeEvent paramServletContextAttributeEvent);//修改,第二次设置同一属性 }
ServletContextAttributeEvent中getName()和getValue()获取设置属性的名称和值
(2)HttpSessionAttributeListener接口
类似ServletContextAttributeListener接口的三个方法,只是参数类型不同
package javax.servlet.http; import java.util.EventListener; public abstract interface HttpSessionAttributeListener extends EventListener { public abstract void attributeAdded(HttpSessionBindingEvent paramHttpSessionBindingEvent);//添加 public abstract void attributeRemoved(HttpSessionBindingEvent paramHttpSessionBindingEvent);//移除 public abstract void attributeReplaced(HttpSessionBindingEvent paramHttpSessionBindingEvent);//替换 }
HttpSessionBindingEvent除了可以获取属性信息外,提供getSession()方法获取当前Session
(3)ServletRequestAttributeListener接口
类似ServletContextAttributeListener接口的三个方法,只是参数类型不同
package javax.servlet; import java.util.EventListener; public abstract interface ServletRequestAttributeListener extends EventListener { public abstract void attributeAdded(ServletRequestAttributeEvent paramServletRequestAttributeEvent);//添加 public abstract void attributeRemoved(ServletRequestAttributeEvent paramServletRequestAttributeEvent);//删除 public abstract void attributeReplaced(ServletRequestAttributeEvent paramServletRequestAttributeEvent);//替换 }
注:以上两种监听器均需要在web.xml中配置,配置如下:
<listener> <listener-class>com.milan.listener.ServletRequestLifeListener</listener-class>
</listener>
Listener的初始化(ServletContentListener初始化)比Servlet和Filter都优先,而销毁比Servlet和Filter都慢
3.感知 Session 绑定的事件监听器
在 Session 域中的对象有以下几种状态:
绑定到 Session 中;
从 Session 域中解除绑定;
随 Session 对象持久化到一个存储设备中;
随 Session 对象从一个存储设备中恢复;
注意:JavaBean对象只有实现了Serializable接口才能活化,否则就只能写到磁盘(钝化),而不能从磁盘读出(活化)
所以继承以下两个接口的Javabean通常也要实现Serializable接口
有两个接口用来了解到Session域中对象的状态:
(1)HttpSessionBindingListener接口
package javax.servlet.http; import java.util.EventListener; public abstract interface HttpSessionBindingListener extends EventListener { public abstract void valueBound(HttpSessionBindingEvent paramHttpSessionBindingEvent); public abstract void valueUnbound(HttpSessionBindingEvent paramHttpSessionBindingEvent); }
监听实现了HttpSessionBindingListener接口的 JavaBean 对象被绑定到Session或从Session中接触绑定的事件
注意:实现这这个接口的类不需要 web.xml 文件中进行注册
不常被使用
(2)HttpSessionActivationListener接口
package javax.servlet.http; import java.util.EventListener; public abstract interface HttpSessionActivationListener extends EventListener { public abstract void sessionWillPassivate(HttpSessionEvent paramHttpSessionEvent);//从磁盘中读取出来 public abstract void sessionDidActivate(HttpSessionEvent paramHttpSessionEvent);//被持久化 }
实现了HttpSessionBindingListener接口的 JavaBean 对象可以感知自己被活化和钝化的事件
在web容器被卸载时,session并未被销毁,而是被持久化了,生成session.src存放在磁盘中,session中的存放的对象也被持久化了,
等web容器再次被装载时,该session又被活化了,存在session中的对象也被写入到内存中
注意:实现这这个接口的类不需要 web.xml 文件中进行注册
三.Listener应用实例
1.HttpSessionListener统计在线人数
2.Spring加载ApplicationContext配置信息
Spring用ContextLoaderListener加载ApplicationContext配置信息,ContextLoaderListener实现了ServletContextListener接口,
从而实现在web容器加载时,装配ApplicationContext的配置信息,如下为ContextLoaderListener的源码:
public class ContextLoaderListener implements ServletContextListener { private ContextLoader contextLoader; /** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { this.contextLoader = createContextLoader(); this.contextLoader.initWebApplicationContext(event.getServletContext());//注意此处 } protected ContextLoader createContextLoader() { return new ContextLoader(); } public ContextLoader getContextLoader() { return this.contextLoader; } public void contextDestroyed(ServletContextEvent event) { if (this.contextLoader != null) { this.contextLoader.closeWebApplicationContext(event.getServletContext()); } } }
这里不粘贴太多实现代码,读者可以具体自己查阅Spring源码
如下为web.xml中的配置:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
注意:(1)上面contextConfigLoaction这个初始化参数可以不配置,但此时意味着默认在WEB-INF目录下的applicationContext.xml
(在MyEclipse中则默认src目录下applicationContext.xml文件)为ApplicationContext的配置信息文件
(2)自定义ApplicationContext的配置信息的文件名,则必须写contextConfigLoaction这个初始化参数
3.Spring配置Log4j日志
Spring用Log4jConfigListener实现Log4j日志的配置,Log4jConfigListener实现了ServletContextListener接口,如下为Log4jConfigListener源码:
public class Log4jConfigListener implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { Log4jWebConfigurer.initLogging(event.getServletContext()); } public void contextDestroyed(ServletContextEvent event) { Log4jWebConfigurer.shutdownLogging(event.getServletContext()); } }
具体不细说,强烈建议看看Spring 源代码
如下为在web.xml中配置
<!-- 设置log4j存放Log文件位置(通过spring统一进行管理) 定义以后,在Web Container启动时将把ROOT的绝对路径写到系统变量里。 然后log4j的配置文件里就可以用${webName.root }来表示Web目录的绝对路径,把log文件存放于webapp中。--> <context-param> <param-name>webAppRootKey</param-name> <param-value>zxoa.root</param-value> </context-param> <!-- 加载log4j的配置文件 --> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:log4j.properties</param-value> </context-param> <!--Spring默认刷新Log4j配置文件的间隔,单位为millisecond --> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <!--Spring用于log4j初始化的监听器 --> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
注意: (1)${webName.root }(表示Web目录的绝对路径)的使用
(2)设置log4jRefreshInterval时间,开一条watchdog线程每隔段时间扫描一下配置文件的变化,所以可以动态的改变记录级别和策略,不需要重启Web应用
4.Spring使用IntrospectorCleanupListener清理缓存
如下为Spring API中对IntrospectorCleanupListener的描述(看不懂就好好学英语吧!):
在web.xml中如下配置:
<!-- Spring刷新Interceptor防止内存泄漏 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener>