首页 > 代码库 > Servlet学习笔记(九):监听器Listener详解

Servlet学习笔记(九):监听器Listener详解

一、简介

(一)概述

1、Listener 用于监听 java web程序中的事件,例如创建、修改、删除Session、request、context等,并触发响应的事件。

2、 Listener 对应观察者模式,事件发生的时候会自动触发该事件对应的Listeer。 Listener 主要用于对 Session、request、context 进行监控。servlet2.5 规范中共有 8 种Listener  。


(二)实现

1、不同功能的Listener 需要实现不同的 Listener  接口,一个Listener也可以实现多个接口,这样就可以多种功能的监听器一起工作。

2、8种监听器可以分为三类:

1)监听 Session、request、context 的创建于销毁,分别为  

HttpSessionLister、ServletContextListener、ServletRequestListener

2)监听对象属性变化,分别为:

HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener 

3)监听Session 内的对象,分别为HttpSessionBindingListener 和 HttpSessionActivationListener。与上面六类不同,这两类 Listener 监听的是Session 内的对象,而非 Session 本身,不需要在 web.xml中配置。


2、实现web.xml的Listener配置。

1)<listener>标签与 <listener-class>

2)<listener>一般配置在 <servlet>便签的前面。


package servlet.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
 * 
 * MyListener.java
 *
 * @title Context监听器
 * @description
 * @author SAM-SHO 
 * @Date 2014-9-25
 */
public class MyListener implements ServletContextListener {

	public void contextDestroyed(ServletContextEvent sce) {
		
	}

	public void contextInitialized(ServletContextEvent sce) {
		
	}

	
}

	<!--监听器 -->
	<listener>
		<listener-class>servlet.listener.MyListener</listener-class>
	</listener>


二、八种类型监听器

(一)监听 Session、request、context 的创建于销毁。

 HttpSessionLister、ServletContextListener、ServletRequestListener

1、三种监听器的触发时机及使用:



2、实例:实现监听对象的创建与销毁

package servlet.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
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;

/**
 * 
 * ListenerTest.java
 * 
 * @title 监听对象的创建与销毁
 * @description
 * @author SAM-SHO
 * @Date 2014-12-10
 */
public class ListenerTest implements HttpSessionListener, ServletContextListener, ServletRequestListener {

	Log log = LogFactory.getLog(getClass());

	// 创建 session
	public void sessionCreated(HttpSessionEvent se) {
		HttpSession session = se.getSession();
		log.info("新创建一个session, ID为: " + session.getId());
	}

	// 销毁 session
	public void sessionDestroyed(HttpSessionEvent se) {
		HttpSession session = se.getSession();
		log.info("销毁一个session, ID为: " + session.getId());
	}

	// 加载 context
	public void contextInitialized(ServletContextEvent sce) {
		ServletContext servletContext = sce.getServletContext();
		log.info("即将启动" + servletContext.getContextPath());
	}

	// 卸载 context
	public void contextDestroyed(ServletContextEvent sce) {
		ServletContext servletContext = sce.getServletContext();
		log.info("即将关闭" + servletContext.getContextPath());
	}

	// 创建 request
	public void requestInitialized(ServletRequestEvent sre) {

		HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();

		String uri = request.getRequestURI();
		uri = request.getQueryString() == null ? uri : (uri + "?" + request.getQueryString());

		request.setAttribute("dateCreated", System.currentTimeMillis());

		log.info("IP " + request.getRemoteAddr() + " 请求 " + uri);
	}

	// 销毁 request
	public void requestDestroyed(ServletRequestEvent sre) {

		HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();

		long time = System.currentTimeMillis() - (Long) request.getAttribute("dateCreated");

		log.info(request.getRemoteAddr() + "请求处理结束, 用时" + time + "毫秒. ");
	}

}


(二)监听对象属性变化,分别为HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener 

1、三种监听器的触发时机及使用:



2、实例:实现对象属性的监听

package servlet.listener;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * 
 * SessionAttributeListenerTest.java
 *
 * @title 监听Session对象的属性
 * @description
 * @author SAM-SHO 
 * @Date 2014-12-10
 */
public class SessionAttributeListenerTest implements HttpSessionAttributeListener {

	Log log = LogFactory.getLog(getClass());

	// 添加属性
	public void attributeAdded(HttpSessionBindingEvent se) {
		HttpSession session = se.getSession();
		String name = se.getName();
		log.info("新建session属性:" + name + ", 值为:" + se.getValue());
	}

	// 删除属性
	public void attributeRemoved(HttpSessionBindingEvent se) {
		HttpSession session = se.getSession();
		String name = se.getName();
		log.info("删除session属性:" + name + ", 值为:" + se.getValue());
	}

	// 修改属性
	public void attributeReplaced(HttpSessionBindingEvent se) {
		HttpSession session = se.getSession();
		String name = se.getName();
		Object oldValue = http://www.mamicode.com/se.getValue();>

(三)监听Session 内的对象,分别为HttpSessionBindingListener 和 HttpSessionActivationListener

1、触发时机及使用:对象必须实现Listener接口,不需要在web.xml中配置




2、实例:实现对象属性的监听

package servlet.listener;

import java.io.Serializable;
import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * 
 * PersonInfo.java
 *
 * @title 同时实现多个接口
 * 被串行化,需要实现Serializable接口
 * @description
 * @author SAM-SHO 
 * @Date 2014-12-10
 */
public class PersonInfo implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable {

	private static final long serialVersionUID = -4780592776386225973L;

	Log log = LogFactory.getLog(getClass());

	private String name;
	private Date dateCreated;


	// 从硬盘加载后
	public void sessionDidActivate(HttpSessionEvent se) {
		HttpSession session = se.getSession();
		log.info(this + "已经成功从硬盘中加载。sessionId: " + session.getId());
	}

	// 即将被钝化到硬盘时
	public void sessionWillPassivate(HttpSessionEvent se) {
		HttpSession session = se.getSession();
		log.info(this + "即将保存到硬盘。sessionId: " + session.getId());
	}

	// 被放进session前
	public void valueBound(HttpSessionBindingEvent event) {
		HttpSession session = event.getSession();
		String name = event.getName();
		log.info(this + "被绑定到session \"" + session.getId() + "\"的" + name + "属性上");

		// 记录放到session中的时间
		this.setDateCreated(new Date());
	}

	// 从session中移除后
	public void valueUnbound(HttpSessionBindingEvent event) {
		HttpSession session = event.getSession();
		String name = event.getName();
		log.info(this + "被从session \"" + session.getId() + "\"的" + name + "属性上移除");
	}

	@Override
	public String toString() {
		return "PersonInfo(" + name + ")";
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getDateCreated() {
		return dateCreated;
	}

	public void setDateCreated(Date dateCreated) {
		this.dateCreated = dateCreated;
	}


}


三、Listener 实例

(一)单态登录:一个账号只能在一台机器上登录。

1、Listener 的代码:

package servlet.listener.singleton;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * LoginSessionListener.java
 *
 * @title 实现单态登录的监听器
 * @description
 * @author SAM-SHO 
 * @Date 2014-12-10
 */
public class LoginSessionListener implements HttpSessionAttributeListener {

	Log log = LogFactory.getLog(this.getClass());

	Map<String, HttpSession> map = new HashMap<String, HttpSession>();

	public void attributeAdded(HttpSessionBindingEvent event) {

		String name = event.getName();

		// 登录
		if (name.equals("personInfo")) {

			PersonInfo personInfo = (PersonInfo) event.getValue();

			if (map.get(personInfo.getAccount()) != null) {

				// map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
				HttpSession session = map.get(personInfo.getAccount());
				PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute("personInfo");//map已经存在的旧的信息

				log.info("帐号" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已经登录,该登录将被迫下线。");

				session.removeAttribute("personInfo");
				session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线。");
			}

			// 将session以用户名为索引,放入map中
			map.put(personInfo.getAccount(), event.getSession());
			log.info("帐号" + personInfo.getAccount() + "在" + personInfo.getIp() + "登录。");
		}
	}

	public void attributeRemoved(HttpSessionBindingEvent event) {

		String name = event.getName();

		// 注销
		if (name.equals("personInfo")) {
			// 将该session从map中移除
			PersonInfo personInfo = (PersonInfo) event.getValue();
			map.remove(personInfo.getAccount());
			log.info("帐号" + personInfo.getAccount() + "注销。");
		}
	}

	public void attributeReplaced(HttpSessionBindingEvent event) {

		String name = event.getName();

		// 没有注销的情况下,用另一个帐号登录
		if (name.equals("personInfo")) {

			// 移除旧的的登录信息
			PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
			map.remove(oldPersonInfo.getAccount());

			// 新的登录信息
			PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute("personInfo");

			// 也要检查新登录的帐号是否在别的机器上登录过
			if (map.get(personInfo.getAccount()) != null) {
				// map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
				HttpSession session = map.get(personInfo.getAccount());
				session.removeAttribute("personInfo");
				session.setAttribute("msg", "您的帐号已经在其他机器上登录,您被迫下线。");
			}
			map.put("personInfo", event.getSession());
		}

	}

}

package servlet.listener.singleton;

import java.io.Serializable;
import java.util.Date;

/**
 * 
 * PersonInfo.java
 *
 * @title 
 * @description
 * @author SAM-SHO 
 * @Date 2014-12-10
 */
public class PersonInfo implements Serializable {

	private static final long serialVersionUID = 4063725584941336123L;

	// 帐号
	private String account;

	// 登录IP地址
	private String ip;

	// 登录时间
	private Date loginDate;

	public String getAccount() {
		return account;
	}

	public void setAccount(String account) {
		this.account = account;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public Date getLoginDate() {
		return loginDate;
	}

	public void setLoginDate(Date loginDate) {
		this.loginDate = loginDate;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((account == null) ? 0 : account.hashCode());
		result = prime * result + ((ip == null) ? 0 : ip.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PersonInfo other = (PersonInfo) obj;
		if (account == null) {
			if (other.account != null)
				return false;
		} else if (!account.equals(other.account))
			return false;
		if (ip == null) {
			if (other.ip != null)
				return false;
		} else if (!ip.equals(other.ip))
			return false;
		return true;
	}



}

	<!-- 单态登录监听器 -->
	<listener>
		<listener-class>servlet.listener.singleton.LoginSessionListener</listener-class>
	</listener>


(二)显示在线人数:会需要3个监听器。

1、ContextListener:获取服务启动的时间等。

2、RequestListener:获取客户端的IP、访问地址,访问次数等。

3、SessionListener:需要监听 Session 的创建与属性变化。

4、代码如下:

package com.helloweenvsfei.listener;

import java.util.Date;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.helloweenvsfei.util.ApplicationConstants;

public class MyContextListener implements ServletContextListener {

	public void contextInitialized(ServletContextEvent event) {
		// 启动时,记录服务器启动时间
		ApplicationConstants.START_DATE = new Date();
	}

	public void contextDestroyed(ServletContextEvent event) {
		// 关闭时,将结果清除。也可以将结果保存到硬盘上。
		ApplicationConstants.START_DATE = null;
		ApplicationConstants.MAX_ONLINE_COUNT_DATE = null;
	}
}


package com.helloweenvsfei.listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class MyRequestListener implements ServletRequestListener {

	public void requestDestroyed(ServletRequestEvent event) {
	}

	public void requestInitialized(ServletRequestEvent event) {

		HttpServletRequest request = (HttpServletRequest) event
				.getServletRequest();

		HttpSession session = request.getSession(true);

		// 记录IP地址
		session.setAttribute("ip", request.getRemoteAddr());

		// 记录访问次数,只记录访问 .html, .do, .jsp, .action 的累计次数
		String uri = request.getRequestURI();
		String[] suffix = { ".html", ".do", ".jsp", ".action" };
		for (int i=0; i<suffix.length; i++) {
			if (uri.endsWith(suffix[i])) {
				break;
			}
			if(i == suffix.length-1)
				return;
		}

		Integer activeTimes = (Integer) session.getAttribute("activeTimes");

		if (activeTimes == null) {
			activeTimes = 0;
		}

		session.setAttribute("activeTimes", activeTimes + 1);
	}
}

package com.helloweenvsfei.listener;

import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import com.helloweenvsfei.util.ApplicationConstants;

public class MySessionListener implements HttpSessionListener,
		HttpSessionAttributeListener {

	public void sessionCreated(HttpSessionEvent sessionEvent) {

		HttpSession session = sessionEvent.getSession();

		// 将 session 放入 map
		ApplicationConstants.SESSION_MAP.put(session.getId(), session);
		// 总访问人数++
		ApplicationConstants.TOTAL_HISTORY_COUNT++;

		// 如果当前在线人数超过历史记录,则更新最大在线人数,并记录时间
		if (ApplicationConstants.SESSION_MAP.size() > ApplicationConstants.MAX_ONLINE_COUNT) {
			ApplicationConstants.MAX_ONLINE_COUNT = ApplicationConstants.SESSION_MAP
					.size();
			ApplicationConstants.MAX_ONLINE_COUNT_DATE = new Date();
		}
	}

	public void sessionDestroyed(HttpSessionEvent sessionEvent) {
		HttpSession session = sessionEvent.getSession();
		// 将session从map中移除
		ApplicationConstants.SESSION_MAP.remove(session.getId());
	}

	public void attributeAdded(HttpSessionBindingEvent event) {

		if (event.getName().equals("personInfo")) {

			// 当前登录用户数++
			ApplicationConstants.CURRENT_LOGIN_COUNT++;
			HttpSession session = event.getSession();

			// 查找该帐号有没有在其他机器上登录
			for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {

				// 如果该帐号已经在其他机器上登录,则以前的登录失效
				if (event.getValue().equals(sess.getAttribute("personInfo"))
						&& session.getId() != sess.getId()) {
					sess.invalidate();
				}
			}
		}
	}

	public void attributeRemoved(HttpSessionBindingEvent event) {

		// 注销 当前登录用户数--
		if (event.getName().equals("personInfo")) {
			ApplicationConstants.CURRENT_LOGIN_COUNT--;
		}
	}

	public void attributeReplaced(HttpSessionBindingEvent event) {

		// 重新登录
		if (event.getName().equals("personInfo")) {
			HttpSession session = event.getSession();
			for (HttpSession sess : ApplicationConstants.SESSION_MAP.values()) {
				// 如果新帐号在其他机器上登录过,则以前登录失效
				if (event.getValue().equals(sess.getAttribute("personInfo"))
						&& session.getId() != sess.getId()) {
					sess.invalidate();
				}
			}
		}
	}

}



Servlet学习笔记(九):监听器Listener详解