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 = se.getValue();
		log.info("修改session属性:" + name + ", 原值:" + oldValue + ", 新值:" + session.getAttribute(name));
	}
}

(三)监听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();
				}
			}
		}
	}

}
时间: 2024-11-09 04:40:30

Servlet学习笔记(九):监听器Listener详解的相关文章

hadoop 学习笔记:mapreduce框架详解

hadoop 学习笔记:mapreduce框架详解 开始聊mapreduce,mapreduce是hadoop的计算框架,我 学hadoop是从hive开始入手,再到hdfs,当我学习hdfs时候,就感觉到hdfs和mapreduce关系的紧密.这个可能是我做技术研究的 思路有关,我开始学习某一套技术总是想着这套技术到底能干什么,只有当我真正理解了这套技术解决了什么问题时候,我后续的学习就能逐步的加快,而学习 hdfs时候我就发现,要理解hadoop框架的意义,hdfs和mapreduce是密不

C#学习笔记二: C#类型详解

前言 这次分享的主要内容有五个, 分别是值类型和引用类型, 装箱与拆箱,常量与变量,运算符重载,static字段和static构造函数. 后期的分享会针对于C#2.0 3.0 4.0 等新特性进行. 再会有三篇博客  这个系列的就会结束了. 也算是自己对园子中@Learning Hard出版的<<C#学习笔记>>的一个总结了. 博客内容基本上都是白天抽空在公司写好的了, 但是由于公司内部网络不能登录博客园所以只能够夜晚拿回来修改,  写的不好或者不对的地方也请各位大神指出. 在下感

Linux学习笔记——用户及权限详解

用户及权限详解    用户.组.权限 安全上下文(secure context): 权限: r   w   x  文件: r:可读,可以使用类似cat等命令查看文件内容: w:可写,可以编辑或删除此文件: X:可执行,exacutable,可以命令提示符下当作命令提交给内核运行:  目录: r:可以对此目录执行ls以列出内部的所有文件: w:可以在此目录创建文件: x:可以使用cd切换进此目录,也可以使用ls -l查看内部文件的详细信息: rwx: r--:只读 r-x:读和执行 ---:无权限

CSS学习笔记(9)--详解CSS中:nth-child的用法

详解CSS中:nth-child的用法 前端的哥们想必都接触过css中一个神奇的玩意,可以轻松选取你想要的标签并给与修改添加样式,是不是很给力,它就是":nth-child". 下面我将用几个典型的实例来给大家讲解:nth-child的实际用途: Tips:还用低版本的IE浏览器的哥们请绕过! :nth-child(2)选取第几个标签,"2可以是你想要的数字" .demo01 li:nth-child(2){background:#090} :nth-child(n

Linux学习笔记—— 用户管理命令详解

 用户管理命令详解  用户管理: useradd,userdel,usermod,passwd,chsh,chfn,finger,id,chage 添加用户: useradd [options] USERNAME    -u (UID)   手动指定UID -g (GID)  (基本组) -G, ...     (附加组) 可以有多个,彼此之间用,号隔开 -c "COMMENT"         注释信息     -d /path/to/somedirectory  指定家目录 -s

IOS学习笔记——ViewController生命周期详解

在我之前的学习笔记中讨论过ViewController,过了这么久,对它也有了新的认识和体会,ViewController是我们在开发过程中碰到最多的朋友,今天就来好好认识一下它.ViewController是IOS开发中MVC模式中的C,ViewController是view的controller,ViewController的职责主要包括管理内部各个view的加载显示和卸载,同时负责与其他ViewController的通信和协调.在IOS中,有两类ViewController,一类是显示内容

[学习笔记]struts.xml配置详解(所有基本配置,包括通配符)

1.<include> 利用include标签,可以将一个struts.xml配置文件分割成多个配置文件,然后在struts.xml中使用<include>标签引入其他配置文件.比如一个网上购物程序,可以把用户配置.商品配置.订单配置分别放在3个配置文件user.xml.goods.xml和order.xml中,然后在struts.xml中将这3个配置文件引入: struts.xml: [html] view plaincopy <?xml version="1.0

【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

这一篇我们将会介绍java中try,catch,finally的用法 以下先给出try,catch用法: try { //需要被检测的异常代码 } catch(Exception e) { //异常处理,即处理异常代码 } 代码区如果有错误,就会返回所写异常的处理. 首先要清楚,如果没有try的话,出现异常会导致程序崩溃.而try则可以保证程序的正常运行下去,比如说: try { int i = 1/0; } catch(Exception e) { ........ } 一个计算的话,如果除数

MyBatis学习笔记2--配置环境详解

1.MyBatis-config.xml详解 一个完整的配置文件如下所示 <configuration> <!-- <properties resource="jdbc.properties"/> --> <properties> <property name="jdbc.driverClassName" value="com.mysql.jdbc.Driver"/> <prope