Java Servlet的监听器Listener,它是实现了javax.servlet.ServletContextListener 接口的服务器端程序,它也是随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。

首先来看一下ServletContextListener接口的源代码:

public abstract interface ServletContextListener extends EventListener{
    public abstract void contextInitialized(ServletContextEvent paramServletContextEvent);
    public abstract void contextDestroyed(ServletContextEvent paramServletContextEvent);
}

 

示例1: 连接数据库池

利用监听器对数据库连接池DataSource的初始化演示它的使用:ListenerTest.java 

package com.mimvp.listen;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.dbcp.BasicDataSource;

/**
 * @author mimvp.com
 */
public class ListenerTest implements ServletContextListener {
	
	// 构造函数
	public ListenerTest() {
		// TODO Auto-generated constructor stub
	}

	// 应用监听器的销毁方法
	@Override
	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		ServletContext servletContext = servletContextEvent.getServletContext();
		// 在整个web应用销毁之前调用,将所有应用空间设置的内容清空
		servletContext.removeAttribute("dataSource");
		System.out.println("销毁工作完成!");
	}

	// 应用监听器的初始化方法
	@Override
	public void contextInitialized(ServletContextEvent servletContextEvent) {
		// 通过事件获取整个应用空间,在整个web应用下面启动时做一些初始化的内容添加工作
		ServletContext servletContext = servletContextEvent.getServletContext();
		
		// 设置一些基本的内容,比如一些参数或一些固定对象
		// 创建DataSource对象、连接池技术 DBCP
		BasicDataSource basicDataSource = new BasicDataSource();
		basicDataSource.setDriverClassName("com.jdbc.Driver");
		basicDataSource.setUrl("jdbc:mysql://localhost:3306/");
		basicDataSource.setUsername("root");
		basicDataSource.setPassword("123456");
		basicDataSource.setMaxActive(10);		// 最大连接数
		basicDataSource.setMaxIdle(5);				// 最大管理数
		basicDataSource.setMaxWait(60);			// 最大等待时间

		// 将 DataSource 放入ServletContext空间里,供整个web应用获取数据库连接
		servletContext.setAttribute("dataSource", basicDataSource);
		System.out.println("应用监听器初始化工作完成....");
		System.out.println("已创建 dataSource");
	}
}

web.xml 配置内容如下:

vim  /com.mimvp.homer/src/main/webapp/WEB-INF/web.xml

  <listener>
    <listener-class>com.mimvp.listen.ListenerTest</listener-class>
  </listener>

配置好后,以后在web应用中就可以通过ServletContext取得BasicDataSource对象,从而获取与数据库的连接,提高性能,方便使用。

启动项目后,在Tomcat服务器打印的日志如下:

[org.springframework.web.context.ContextLoader] - Root WebApplicationContext: initialization started
[org.springframework.web.context.support.XmlWebApplicationContext] - Refreshing Root WebApplicationContext: startup date [Fri Apr 28 11:23:38 CST 2017]; root of context hierarchy
[org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from class path resource [spring-mybatis.xml]
[org.springframework.beans.factory.config.PropertyPlaceholderConfigurer] - Loading properties file from class path resource [jdbc.properties]
[org.springframework.web.context.ContextLoader] - Root WebApplicationContext: initialization completed in 864 ms

应用监听器初始化工作完成....
已创建 dataSource

 

示例2: 获取项目路径

package com.mimvp.listen;

import java.io.File;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ListenerTest2 implements ServletContextListener {

	public ListenerTest2() {
		// TODO Auto-generated constructor stub
	}

	// 销毁监听器
	@Override
	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		System.out.println("dataDestroyed: " + servletContextEvent.getServletContext());
	}

	// 初始化监听器
	@Override
	public void contextInitialized(ServletContextEvent servletContextEvent) {
		try {
			String basePath = servletContextEvent.getServletContext().getRealPath("/");
			System.out.println("basePath: " + basePath);
			// basePath: E:\myPro\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\com.mimvp.homer\

			if(!(basePath.endsWith(File.separator))) {
				basePath = basePath + File.separator;
			}
			basePath = basePath + "WEB-INF" + File.separator + "classes" + File.separator;
			System.out.println("basePath2: " + basePath);  
			// basePath2: E:\myPro\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\com.mimvp.homer\WEB-INF\classes\
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(-1);
		}
	}
}

 

示例3: Session 监听

package com.mimvp.listen;

import javax.servlet.ServletContext;
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;

class OnlineUserMonitorClient {
	public OnlineUserMonitorClient(){
	}
	
	public boolean afterSessionDestroyed(HttpSession httpSession) {
		httpSession.removeAttribute("onlineUserMonitorClient");
		return true;
	}
}

public class ListenerUser implements HttpSessionListener {
	protected final Log log = LogFactory.getLog(super.getClass());
			
	// 构造函数
	public ListenerUser() {
		// TODO Auto-generated constructor stub
	}

	// 创建Session
	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		this.log.error("Session create id = " + httpSessionEvent.getSession().getId());
	}

	// 销毁Session
	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		this.log.error("Session destory id = " + httpSessionEvent.getSession().getId());
		HttpSession httpSession = httpSessionEvent.getSession();
		ServletContext servletContext = httpSession.getServletContext();
		ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
		OnlineUserMonitorClient client = (OnlineUserMonitorClient)context.getBean("onlineUserMonitorClient");  
        client.afterSessionDestroyed(httpSession);  
	}
}

 

监听器在实际项目中的应用,监听器在java web中应用的较多,

比如:统计当前在线人数、自定义session扫描器。

应用1:统计当前在线人数

package com.mimvp.listen;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionListener implements HttpSessionListener {
	public static int TOTAL_ONLINE_USERS = 0;

	public SessionListener() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
		TOTAL_ONLINE_USERS = (Integer)servletContext.getAttribute("TOTAL_ONLINE_USERS");
		// 如果用户退出,TOTAL_ONLINE_USERS自加1  
		if(0 == TOTAL_ONLINE_USERS){
			servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
		}
		else {
			TOTAL_ONLINE_USERS++;
			servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
		}
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		ServletContext servletContext = httpSessionEvent.getSession().getServletContext();
		TOTAL_ONLINE_USERS = (Integer)servletContext.getAttribute("TOTAL_ONLINE_USERS");
		// 如果用户退出,TOTAL_ONLINE_USERS自减1  
		if(0 == TOTAL_ONLINE_USERS){
			servletContext.setAttribute("TOTAL_ONLINE_USERS", 1);
		}
		else {
			TOTAL_ONLINE_USERS--;
			servletContext.setAttribute("TOTAL_ONLINE_USERS", TOTAL_ONLINE_USERS);
		}
	}
}

 

 

应用2:自定义session扫描器

SessionScanerListener.java

package com.mimvp.listen;

import java.util.Collections;
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;

/**
 * 当网站用户量增加时,session占用的内存会越来越大,这时session的管理,将会是一项很大的系统开销,
 * 为了高效的管理session,可以写一个监听器,定期清理掉过期的session 
 */
public class SessionScanerListener implements HttpSessionListener, ServletContextListener {
	// 创建一个线程安全的集合,用来存储session  
	List<HttpSession> sessionList = Collections.synchronizedList(new LinkedList<HttpSession>());
	private Object lock = new Object();		// 锁对象
	
	public SessionScanerListener() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void sessionCreated(HttpSessionEvent httpSessionEvent) {
		System.out.println("session 创建中...");
		HttpSession httpSession = httpSessionEvent.getSession();
		synchronized (lock) {
			sessionList.add(httpSession);
		}
		System.out.println("session 创建成功!");
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
		System.out.println("session 销毁成功");
	}

	// web应用启动时触发
	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		System.out.println("web 应用初始化...");
		Timer timer = new Timer();
		timer.schedule(new MyTask(sessionList,lock), 0, 1000*30);  
	}
	
	 // web应用关闭时触发
	@Override
	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		System.out.println("web 应用关闭");
	}
}

 

MyTask.java

package com.mimvp.listen;

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> sessionList;
	private Object lock;		// 存储传递过来的锁  
	
	// 构造方法 
	MyTask(List<HttpSession> list, Object lock){
		this.sessionList = list;
		this.lock = lock;
	}
	
	@Override
	public void run() {
		// 考虑到多线程的情况,这里必须要同步
		synchronized (lock) {
			System.out.println("定时器开始执行...");
			ListIterator<HttpSession> listIterator = sessionList.listIterator();
			while(listIterator.hasNext()) {
				HttpSession httpSession = listIterator.next();
				// httpSession.getLastAccessedTime() = session的最后访问时间 
				long waitTime = System.currentTimeMillis() - httpSession.getLastAccessedTime();
				if(waitTime > 1000 * 30) {
					httpSession.invalidate();		// 手动销毁session  
					listIterator.remove();			// 从集合中移除已经被销毁的session
				}
			}
		}
	}
}