引言:listener顾名思义就是监听的意思,作用就是监听程序中的一些变化,并根据其做出一些相应的响应。通俗的语言就是在session,request,application三个对象创建消亡或者往其中进行修改增加删除属性时自动执行代码的功能组件。
定义:listener监听器主要是实现了javax.servlet.ServletContextListener接口的服务器端程序,它跟过滤器一样随web应用启动而启动,只需要初始化一次,以后都可以进行监听。一般主要用于ServletContext、HttpSession、HttpServletSession这三个对象中的属性变更信息事件监听。
使用listener步骤:
- 通过实现具体接口创建实现类(可实现多个监听器接口)
- 配置实现类成为监听器,有两种配置方式:
- 直接用@WebListener注解修饰实现类
- 通过web.xml方式配置,代码如下:
<listener>
<listener-class>com.zrgk.listener.MyListener</lisener-class>
</listener>
一、对request进行监听
应用实例:实现对javax.servlet.ServletRequestListener接口的监听,也就是HttpRequestServlet进行监听。
1、Web.xml中的配置:
<listener>
-
<description>HttpRequestListner监听器</description>
-
<listener-class>com.check.listener.MyRequestContextListener</listener-class>
-
</listener>
2、在MyRequestContextListener类中对ServletRequestListener接口的实现
-
package com.check.listener;
-
import javax.servlet.ServletRequestEvent;
-
import javax.servlet.ServletRequestListener;
-
import javax.servlet.http.HttpSessionEvent;
-
import javax.servlet.http.HttpSessionListener;
-
public class MyRequestContextListener implements ServletRequestListener{
-
@Override
-
public void requestDestroyed(ServletRequestEvent sre) {
-
System.out.println("Request销毁成功"+sre.getServletRequest());
-
}
-
@Override
-
public void requestInitialized(ServletRequestEvent sre) {
-
System.out.println("Request创建成功"+sre.getServletRequest());
-
}
-
}
总结这:样就可以对request请求进行监听,其中对象的创建和销毁时间为:request请求发生的时候对象创建,当响应产生的时候request对象销毁。
二、对session进行监听
1、web.xml中的配置
-
<!--注册针对HttpSession对象监听器 -->
-
<listener>
-
<description>HttpSessionListener监听器</description>
-
<listener-class>com.check.listener.MySessionContextListener</listener-class>
-
</listener>
2、MySessionContextListener对HttpSessionListener实现
-
package com.check.listener;
-
import java.text.SimpleDateFormat;
-
import java.util.Date;
-
import javax.servlet.ServletContextEvent;
-
import javax.servlet.ServletContextListener;
-
import javax.servlet.http.HttpSessionEvent;
-
import javax.servlet.http.HttpSessionListener;
-
public class MySessionContextListener implements HttpSessionListener {
-
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
@Override
-
public void sessionCreated(HttpSessionEvent se) {
-
Date date=new Date();
-
System.out.println("session创建成功"+se.getSession()+" "+sdf.format(date));
-
}
-
@Override
-
public void sessionDestroyed(HttpSessionEvent se) {
-
Date date=new Date();
-
System.out.println("session销毁成功"+se.getSession() + " "+sdf.format(date));
-
}
-
}
总结:我在运行的时候,报这个session创建的时间是在一次Httprequest请求成功后创建的,也就是相当于一个request请求会创建一个session,而session的创建包括如下几种情况。
1.session对象创建:
reqeust.getSession();它是用于获取session.
是否创建,分以下几种情况:
1.请求中如果没有jsessionid,那么就是创建session对象。
2.如果请求头中有jsessionid值:
1.如果在服务器端,有一个session的id值与其一样,不创建,直接使用。
2.如果在服务器端,没有这个session的id值,那么会创建。
2. session销毁:
1.默认超时 30分钟
2.设置session超时时间
setMaxInactiveInterval(int interval)
3.invalidate()手动销毁.
4.关闭服务器
而我在创建的时候,在web.xm中配置session的销毁时间:1分钟
-
<session-config>
-
<session-timeout>1</session-timeout>
-
</session-config>
三、实现对ServletContext事件的监听
1、web.xml配置
-
<listener>
-
<description>ServletContextListener监听器</description>
-
<!-- 实现了ServletContextListener接口的监听器类 -->
-
<listener-class>com.check.listener.MyServletContextListener</listener-class>
-
</listener>
2、实现类MyServletContextListener;
-
package com.check.listener;
-
import javax.servlet.ServletContextEvent;
-
import javax.servlet.ServletContextListener;
-
public class MyServletContextListener implements ServletContextListener {
-
@Override
-
public void contextInitialized(ServletContextEvent sce) {
-
System.out.println("ServletContext 对象创建"+sce.getServletContext());
-
}
-
@Override
-
public void contextDestroyed(ServletContextEvent sce) {
-
System.out.println("ServletContext 对象销毁"+sce.getServletContext());
-
}
-
}
四、对其中属性进行一个监听:例如ServletContextAttributeListener
1、web.xml配置
-
<listener>
-
<description>MyServletContextAttributeListener监听器</description>
-
<listener-class>com.check.listener.MyServletContextAttributeListener</listener-class>
-
</listener>
2、实现类
-
package com.check.listener;
-
import java.text.MessageFormat;
-
import javax.servlet.ServletContextAttributeEvent;
-
import javax.servlet.ServletContextAttributeListener;
-
import javax.servlet.ServletContextListener;
-
public class MyServletContextAttributeListener implements ServletContextAttributeListener{
-
@Override
-
public void attributeAdded(ServletContextAttributeEvent scab) {
-
String str =MessageFormat.format(
-
"ServletContext域对象中添加了属性:{0},属性值是:{1}"
-
,scab.getName()
-
,scab.getValue());
-
System.out.println(str);
-
}
-
@Override
-
public void attributeRemoved(ServletContextAttributeEvent scab) {
-
String str =MessageFormat.format(
-
"ServletContext域对象中删除属性:{0},属性值是:{1}"
-
,scab.getName()
-
,scab.getValue());
-
System.out.println(str);
-
}
-
@Override
-
public void attributeReplaced(ServletContextAttributeEvent scab) {
-
String str =MessageFormat.format(
-
"ServletContext域对象中替换了属性:{0}的值"
-
,scab.getName());
-
System.out.println(str);
-
}
-
}
五、ServletContextListener原理
Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动
而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。首先来看一下ServletContextListener接口的源代码:
[java] view plain copy
- public abstract interface ServletContextListener extends EventListener{
- public abstract void contextInitialized(ServletContextEvent paramServletContextEvent);
- public abstract void contextDestroyed(ServletContextEvent paramServletContextEvent);
- }
下面利用监听器对数据库连接池DataSource的初始化演示它的使用:ListenerTest.java
[java] view plain copy
- import javax.servlet.ServletContext;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import org.apache.commons.dbcp.BasicDataSource;
- /**
- * 现在来说说Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的
- * 服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化
- * 的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。
- *
- * 示例代码:使用监听器对数据库连接池DataSource进行初始化
- */
- public class ListenerTest implements ServletContextListener{
- // 应用监听器的销毁方法
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
- ServletContext servletContext = servletContextEvent.getServletContext();
- // 在整个web应用销毁之前调用,将所有应用空间所设置的内容清空
- servletContext.removeAttribute("dataSource");
- System.out.println("销毁工作完成...");
- }
- // 应用监听器的初始化方法
- public void contextInitialized(ServletContextEvent servletContextEvent) {
- // 通过这个事件可以获取整个应用的空间
- // 在整个web应用下面启动的时候做一些初始化的内容添加工作
- ServletContext servletContext = servletContextEvent.getServletContext();
- // 设置一些基本的内容;比如一些参数或者是一些固定的对象
- // 创建DataSource对象,连接池技术 dbcp
- BasicDataSource basicDataSource = new BasicDataSource();
- basicDataSource.setDriverClassName("com.jdbc.Driver");
- basicDataSource.setUrl("jdbc:mysqlocalhost:3306/");
- basicDataSource.setUsername("root");
- basicDataSource.setPassword("root");
- basicDataSource.setMaxActive(10);//最大连接数
- basicDataSource.setMaxIdle(5);//最大管理数
- //bds.setMaxWait(maxWait); 最大等待时间
- // 把 DataSource 放入ServletContext空间中,
- // 供整个web应用的使用(获取数据库连接)
- servletContext.setAttribute("dataSource", basicDataSource);
- System.out.println("应用监听器初始化工作完成...");
- System.out.println("已经创建DataSource...");
- }
- }
web.xml中配置如下,很简单:
[html] view plain copy
- <!-- 配置应用监听器 -->
- <listener>
- <listener-class>com.ycq.ListenerTest</listener-class>
- </listener>
这样配置好了之后,以后在web应用中就可以通过ServletContext取得BasicDataSource对象,从而获取与数据库的连接,提高性能,方便使用。
示例代码二:
[java] view plain copy
- import java.io.File;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import com.i2f.fsp.deploy.TransactionDeployer;
- /**
- * 监听器随着项目的启动而启动
- *
- */
- public class ListenerTest2 implements ServletContextListener{
- // 销毁监听器
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
- System.out.println("date20161020095500 :" + servletContextEvent.getServletContext());
- }
- public void contextInitialized(ServletContextEvent servletContextEvent) {
- try{
- // 获取项目跟路径
- String basePath = servletContextEvent.getServletContext().getRealPath("/");
- // D:\apache-tomcat-6.0.41\webapps\i2money\ 绝对路径
- System.out.println("basePath20161020094700 :" + basePath);
- if (!(basePath.endsWith(File.separator))){
- basePath = basePath + File.separator;
- }
- basePath = basePath + "WEB-INF" + File.separator + "classes" + File.separator;
- new TransactionDeployer(basePath).deploy();
- // D:\apache-tomcat-6.0.41\webapps\i2money\WEB-INF\classes\
- System.out.println("basePath20161020094701 :" + basePath);
- }
- catch (Exception e){
- e.printStackTrace();
- System.exit(-1);
- }
- }
- }
六、HttpSessionListener示例代码
示例代码三:
[java] view plain copy
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.context.ApplicationContext;
- import org.springframework.web.context.support.WebApplicationContextUtils;
- public class UserLogoutListener implements HttpSessionListener{
- protected final Log log = LogFactory.getLog(super.getClass());
- public void sessionCreated(HttpSessionEvent event){
- this.log.error("session created. id = " + event.getSession().getId());
- }
- public void sessionDestroyed(HttpSessionEvent event){
- this.log.error("session destroyed.id = " + event.getSession().getId());
- HttpSession session = event.getSession();
- ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(session.getServletContext());
- OnlineUserMonitorClient client = (OnlineUserMonitorClient)context.getBean("onlineUserMonitorClient");
- client.afterSessionDestroyed(session);
- }
- }
监听器在实际项目中的应用,监听器在java web中应用的较多,比如:统计当前在线人数、自定义session扫描器。
--------------------- 应用一:统计当前在线人数 ---------------------
[java] view plain copy
- import javax.servlet.ServletContext;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- /**
- * @description HttpSessionListener监听器实现统计网站在线人数的功能
- */
- public class SessionListener implements HttpSessionListener{
- public static int TOTAL_ONLINE_USERS = 0;
- public void sessionCreated(HttpSessionEvent httpSessionEvent) {
- ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
- TOTAL_ONLINE_USERS = (Integer) servletContext.getAttribute("TOTAL_ONLINE_USERS");
- // 如果用户退出,TOTAL_ONLINE_USERS自减1
- if(TOTAL_ONLINE_USERS == 0){
- servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
- }
- else{
- TOTAL_ONLINE_USERS--;
- servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
- }
- }
- public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
- ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
- TOTAL_ONLINE_USERS = (Integer) servletContext.getAttribute("TOTAL_ONLINE_USERS");
- // 如果用户登录,TOTAL_ONLINE_USERS自增1
- if(TOTAL_ONLINE_USERS == 0){
- servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
- }
- else{
- TOTAL_ONLINE_USERS++;
- servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
- }
- }
- }
--------------------- 应用二:自定义session扫描器 ---------------------
[java] view plain copy
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Timer;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- import jeus.util.concurrent50.Collections;
- /**
- * @description 当网站用户量增加时,session占用的内存会越来越大,这时session的管理,将会是一项很大的
- * 系统开销,为了高效的管理session,我们可以写一个监听器,定期清理掉过期的session
- */
- public class SessionScanerListener implements HttpSessionListener,ServletContextListener{
- // 创建一个线程安全的集合,用来存储session
- @SuppressWarnings("unchecked")
- List<HttpSession> sessionList = Collections.synchronizedList(new LinkedList<HttpSession>());
- private Object lock = new Object();
- public void sessionCreated(HttpSessionEvent httpSessionEvent) {
- System.out.println("session 创建成功...");
- HttpSession httpSession = httpSessionEvent.getSession();
- synchronized (lock){
- sessionList.add(httpSession);
- }
- }
- public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
- System.out.println("session 销毁成功...");
- }
- // web应用关闭时触发contextDestroyed事件
- public void contextDestroyed(ServletContextEvent servletContextEvent) {
- System.out.println("web应用关闭...");
- }
- // web应用启动时触发contextInitialized事件
- public void contextInitialized(ServletContextEvent servletContextEvent) {
- System.out.println("web应用初始化...");
- // 创建定时器
- Timer timer = new Timer();
- // 每隔30秒就定时执行任务
- timer.schedule(new MyTask(sessionList,lock), 0, 1000*30);
- }
- }
[java] view plain copy
- import java.util.List;
- import java.util.ListIterator;
- import java.util.TimerTask;
- import javax.servlet.http.HttpSession;
- /**
- * 定时器,定义定时任务的具体内容
- */
- public class MyTask extends TimerTask{
- private List<HttpSession> list;
- // 存储传递过来的锁
- private Object lock;
- // 构造方法
- MyTask(List<HttpSession> list, Object lock){
- this.list = list;
- this.lock = lock;
- }
- @Override
- public void run() {
- // 考虑到多线程的情况,这里必须要同步
- synchronized (lock){
- System.out.println("定时器开始执行...");
- ListIterator<HttpSession> listIterator = list.listIterator();
- while(listIterator.hasNext()){
- HttpSession httpSession = listIterator.next();
- // httpSession.getLastAccessedTime() = session的最后访问时间
- if(System.currentTimeMillis() - httpSession.getLastAccessedTime() > 1000*30){
- // 手动销毁session
- httpSession.invalidate();
- // 从集合中移除已经被销毁的session
- listIterator.remove();
- }
- }
- }
- }
- }
七、spring常用listener
1、IntrospectorCleanupListener
IntrospectorCleanupListener应该注册为web.xml中的第一个Listener,在任何其他Listener之前注册。 该监听器在web.xml中的配置如下:
Xml代码
- <listener>
- <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
- </listener>
在Web应用程序关闭时IntrospectorCleanupListener将会刷新JDK的JavaBeans的Introspector缓存。在你的web.xml中注册这个listener来确保Web应用程序的类加载器以及其加载的类正确的释放资源。
如果JavaBeans的Introspector已被用来分析应用程序类,系统级的Introspector缓存将持有这些类的一个硬引用。因此,这些类和Web应用程序的类加载器在Web应用程序关闭时将不会被垃圾收集器回收!而IntrospectorCleanupListener则会对其进行适当的清理,已使其能够被垃圾收集器回收。
2、Log4jConfigListener
Spring提供了一个Log4jConfigListener,本身就能通过web.xml中配置来指定位置加载log4j配置文件和log输出路径,但是该 listener需要放在spring的Listener之前。
使用spring中的Log4jConfigListener有如如下好处:
1、动态的改变日志级别和策略,不需要重启Web应用。
2、把log文件定在 /WEB-INF/logs/ 而不需要写绝对路径。
系统把web目录的路径压入一个叫webapp.root的系统变量,日志保存路径就可以用相对路径了
log4j.appender.logfile.File=${webapp.root}/WEB-INF/logs/monia.log
3、可以把log4j.properties和其他properties一起放在/WEB-INF/ ,而不是Class-Path。
4、log4jRefreshInterval为6000表示开一条watchdog线程每6秒扫描一下配置文件的变化。
在web.xml文件中添加如下配置:
Xml代码
- <!-- 当同一个应用服务器中部署两个以上用到Log4jConfigListener的应用时,需要通过该参数加以区别 -->
- <context-param>
- <param-name>webAppRootKey</param-name>
- <param-value>some.web.root</param-value>
- </context-param>
- <context-param>
- <param-name>log4jConfigLocation</param-name>
- <param-value>WEB-INF/log4j.properties</param-value>
- </context-param>
- <context-param>
- <param-name>log4jRefreshInterval</param-name>
- <param-value>60000</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
- </listener>
3、WebAppRootListener
这个监听器就会在web上下文初始化的时候,调用webUtil的对应方法,首先获取到param-name对应的param-value,然后根据传递进去的ServletContext对象得到web的物理路径:String root = servletContext.getRealPath("/");接着把这个param-value作为key,root作为value放到system中System.setProperty(key, root);然后在web中可以用 System.get.....就可以得到web的跟目录的物理路径了。
监听web.xml中的配置para-name为webAppRootKey的值,
Xml代码
- <context-param>
- <param-name>webAppRootKey</param-name>
- <param-value>some.web.root</param-value>
- </context-param>
再配置该监听器
Xml代码
- <listener>
- <listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
- </listener>
如果在web.xml中已经配置了Log4jConfigListener监听器,则不需要配置WebAppRootListener了。因为Log4jConfigListener已经包含了WebAppRootListener的功能。
4、ContextLoaderListener
启动Web容器时,自动装配ApplicationContext的配置信息。该监听器在web.xml中的配置如下:
Xml代码
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
如果在web.xml中不写任何参数配置信息,默认的路径是/WEB-INF/applicationContext.xml,在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml;如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
Xml代码
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/classes/applicationContext-*.xml,
- claaspath:/applicationContext-*.xml
- </param-value>
- </context-param>
5、RequestContextListener
在Spring2.0中除了以前的Singleton和Prototype外又加入了三个新的web作用域,分别为request、session和global session。
Request作用域:
针对每次HTTP请求,Spring容器都会创建一个全新的bean实例,且该bean实例仅在当前HTTP request内有效。当处理请求结束,request作用域的bean实例将被销毁。
Session作用域:
针对某个HTTP Session,Spring容器会创建一个全新的bean实例,且该bean仅在当前HTTP Session内有效。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
global session作用域:
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
作用域依赖问题:如果将Web相关作用域的Bean注入到singleton或prototype的Bean中,这种情况下,需要Spring AOP
Xml代码
- <bean name="car" class="com.demo.Car" scope="request">
- <aop:scoped-proxy/>
- </bean>
- <bean id="boss" class="com.demo.Boss" >
- <properrty name="car" ref="car" />
- </bean>
如果想让容器里的某个bean拥有其中某种新的web作用域,除了在bean级上配置相应的scope属性,还必须在容器级做一个额外的初始化配置。该监听器在web.xml中的配置如下:
Xml代码
- <listener>
- <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
- </listener>