文章目录
简介
概述
Listener
用于监听 java web 程序中的事件,例如创建、修改、删除Session、request、context
等,并触发响应的事件Listener
对应观察者模式,事件发生的时候会自动触发该事件对应的Listeer
。 Listener 主要用于对Session、request、context
进行监控。servlet2.5 规范中共有 8 种Listener
自定义实现
实现接口
- 不同功能的 Listener 需要实现不同的 Listener 接口,一个Listener也可以实现多个接口,这样就可以多种功能的监听器一起工作
- 8种监听器可以分为三类:
- 监听
Session、request、context
的创建与销毁,分别为HttpSessionLister、ServletContextListener、ServletRequestListener
- 监听对象属性变化,分别为:
HttpSessionAttributeLister、ServletContextAttributeListener、ServletRequestAttributeListener
- 监听Session 内的对象, 分别为
HttpSessionBindingListener
和HttpSessionActivationListener
。与上面两类类不同,这两个 Listener 监听的是Session 内的对象,而非 Session 本身,不需要在 web.xml 中配置
配置
listener
和listener-class
标签listener
一般配置在servlet
标签的前面
自定义实现如下
- 代码
public class CustomListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听创建");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("监听销毁");
}
}
- 配置
<!--自定义监听器 -->
<listener>
<listener-class>com.learning.servlet2x.listener.CustomListener</listener-class>
</listener>
监听器详解(8个)
监听 Session、request、context 的创建和销毁
-
ServletContextListener
: 监听 Context 的创建和销毁,可用于启动时获取 web.xml里配置的初始化参数、获取字典表数据等 -
HttpSessionLister
: 监听 Session 的创建与销毁。可以统计session的个数,从而记录在线者信息 -
ServletRequestListener
: 监听 Request 的创建与销毁。用户每次请求request都会执行该监听器的requestInitialized
方法。 -
测试
- 自定义一个监听器,分别实现以上三个接口, 具体代码底部源码链接
public class ObjectListener implements HttpSessionListener, ServletContextListener, ServletRequestListener
监听对象属性变化
-
ServletContextAttributeListener
:监听 Context 中属性的变化,如添加、更新、删除 -
HttpSessionAttributeLister
:监听 Session 中属性的变化,如添加、更新、删除 -
ServletRequestAttributeListener
:监听 Request 中属性的变化,如添加、更新、删除 -
测试
- 自定义一个监听器,分别实现以上三个接口, 具体代码底部源码链接
public class ObjectAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener
监听 Session 内的对象(不需要web的配置、对象必须实现Listener接口)
HttpSessionBindingListener
:对象被放入、取出 Session 被触发HttpSessionActivationListener
:Session里面的内容保存到硬盘,或者从硬盘读取- 测试
- 自定义一个监听器,分别实现以上2个接口, 具体代码底部源码链接
public class SessionObjectListener implements HttpSessionActivationListener, HttpSessionBindingListener, Serializable
常用监听器
单态登录:一个账号只能在一台机器上登录:HttpSessionAttributeListener
public class LoginSessionListener implements HttpSessionAttributeListener {
/**
* 保存 Session
*/
Map<String, HttpSession> map = new HashMap<String, HttpSession>();
/**
* Session 属性添加
*
* @param event
*/
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
// 被添加进来的属性名称
String name = event.getName();
// 登录
if (name.equals(PersonInfo.SESSION_PERSON)) {
PersonInfo personInfo = (PersonInfo) event.getValue();
// map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
HttpSession session = map.get(personInfo.getAccount());
if (session != null) {
//map 已经存在的旧的信息
PersonInfo oldPersonInfo = (PersonInfo) session.getAttribute(PersonInfo.SESSION_PERSON);
System.out.println("帐号" + oldPersonInfo.getAccount() + "在" + oldPersonInfo.getIp() + "已经登录,该登录将被迫下线。");
session.removeAttribute(PersonInfo.SESSION_PERSON);
session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录,您被迫下线。");
}
// 将session以用户名为索引,放入map中
map.put(personInfo.getAccount(), event.getSession());
System.out.println("帐号" + personInfo.getAccount() + "在" + personInfo.getIp() + "登录。");
}
}
/**
* Session 属性删除
*
* @param event
*/
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
String name = event.getName();
// 注销
if (name.equals(PersonInfo.SESSION_PERSON)) {
// 将该 session 从 map 中移除
PersonInfo personInfo = (PersonInfo) event.getValue();
map.remove(personInfo.getAccount());
System.out.println("帐号" + personInfo.getAccount() + "注销。");
}
}
/**
* Session 属性修改
* 没有注销的情况下,用另一个帐号登录
*
* @param event
*/
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
// 被修改的属性名
String name = event.getName();
// 没有注销的情况下,用另一个帐号登录
if (name.equals(PersonInfo.SESSION_PERSON)) {
// 移除旧的的登录信息
PersonInfo oldPersonInfo = (PersonInfo) event.getValue();
map.remove(oldPersonInfo.getAccount());
// 新的登录信息
PersonInfo personInfo = (PersonInfo) event.getSession().getAttribute(PersonInfo.SESSION_PERSON);
// 也要检查新登录的帐号是否在别的机器上登录过
if (map.get(personInfo.getAccount()) != null) {
// map 中有记录,表明该帐号在其他机器上登录过,将以前的登录失效
HttpSession session = map.get(personInfo.getAccount());
session.removeAttribute(PersonInfo.SESSION_PERSON);
session.setAttribute(PersonInfo.SESSION_MSG, "您的帐号已经在其他机器上登录,您被迫下线。");
}
map.put(PersonInfo.SESSION_PERSON, event.getSession());
}
}
class PersonInfo implements Serializable {
private static final long serialVersionUID = 4063725584941336123L;
public static final String SESSION_PERSON = "_PERSON_";
public static final String SESSION_MSG = "_MSG_";
/**
* 帐号
*/
private String account;
/**
* 登录IP地址
*/
private String ip;
/**
* 登录时间
*/
private Date loginDate;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PersonInfo that = (PersonInfo) o;
return Objects.equals(account, that.account) &&
Objects.equals(ip, that.ip) &&
Objects.equals(loginDate, that.loginDate);
}
@Override
public int hashCode() {
return Objects.hash(account, ip, 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;
}
}
}
显示在线人数: 在线用户列表、总访问人数等功能
OnlineSessionListener
: 在线人数监听器:监听Session对象属性变化,监听Session 属性变化。需要记录在线用户列表、总访问人数等
public class OnlineSessionListener implements HttpSessionListener, HttpSessionAttributeListener {
/**
* 监听 Session 创建
*
* @param sessionEvent
*/
@Override
public void sessionCreated(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
// 将 session 放入 map
OnlineConstants.SESSION_MAP.put(session.getId(), session);
// 总访问人数++
OnlineConstants.TOTAL_HISTORY_COUNT++;
// 如果当前在线人数超过历史记录,则更新最大在线人数,并记录时间
int sessionCount = OnlineConstants.SESSION_MAP.size();
if (sessionCount > OnlineConstants.MAX_ONLINE_COUNT) {
OnlineConstants.MAX_ONLINE_COUNT = sessionCount;
OnlineConstants.MAX_ONLINE_COUNT_DATE = new Date();
}
}
/**
* 监听Session 销毁
*
* @param sessionEvent
*/
@Override
public void sessionDestroyed(HttpSessionEvent sessionEvent) {
HttpSession session = sessionEvent.getSession();
// 将session从map中移除
OnlineConstants.SESSION_MAP.remove(session.getId());
}
/**
* 监听 Session 属性添加
*
* @param event
*/
@Override
public void attributeAdded(HttpSessionBindingEvent event) {
if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {
// 当前登录用户数++
OnlineConstants.CURRENT_LOGIN_COUNT++;
HttpSession session = event.getSession();
// 查找该帐号有没有在其他机器上登录
for (HttpSession oldSession : OnlineConstants.SESSION_MAP.values()) {
// 如果该帐号已经在其他机器上登录,则以前的登录失效
if (event.getValue().equals(oldSession.getAttribute(OnlineConstants.SESSION_PERSON))
&& session.getId() != oldSession.getId()) {
oldSession.invalidate();
}
}
}
}
/**
* 监听 Session 属性删除
*
* @param event
*/
@Override
public void attributeRemoved(HttpSessionBindingEvent event) {
// 注销 当前登录用户数--
if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {
OnlineConstants.CURRENT_LOGIN_COUNT--;
}
}
/**
* 监听 Session 属性修改
*
* @param event
*/
@Override
public void attributeReplaced(HttpSessionBindingEvent event) {
// 重新登录
if (event.getName().equals(OnlineConstants.SESSION_PERSON)) {
HttpSession session = event.getSession();
for (HttpSession sess : OnlineConstants.SESSION_MAP.values()) {
// 如果新帐号在其他机器上登录过,则以前登录失效
if (event.getValue().equals(sess.getAttribute("personInfo"))
&& session.getId() != sess.getId()) {
sess.invalidate();
}
}
}
}
}
OnlineRequestListener
: 在线人数监听器:监听 ServletRequest 创建与销毁。统计单用户,访问次数。
public class OnlineRequestListener implements ServletRequestListener {
/**
* 每一个请求都会触发
*
* @param event
*/
@Override
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);
}
@Override
public void requestDestroyed(ServletRequestEvent event) {
}
}
OnlineContextListener
:在线人数监听器:监听 ServletContext 启动停止
public class OnlineContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// 启动时,记录服务器启动时间
OnlineConstants.START_DATE = new Date();
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// 关闭时,将结果清除。也可以将结果保存到硬盘上。
OnlineConstants.START_DATE = null;
OnlineConstants.MAX_ONLINE_COUNT_DATE = null;
}
}
OnlineConstants
: 在线人数监听器:需要监听的类型信息
public class OnlineConstants {
/**
* 索引所有的 Session
* 1、Session创建的时候,放入map
* 2、Session销毁的时候,从map中删除
*/
public static Map<String, HttpSession> SESSION_MAP = new HashMap<>();
/**
* 当前登录的用户总数
*/
public static int CURRENT_LOGIN_COUNT;
/**
* 历史访客
*/
public static int TOTAL_HISTORY_COUNT;
/**
* 服务器启动时间
*/
public static Date START_DATE;
/**
* 最高在线时间
*/
public static Date MAX_ONLINE_COUNT_DATE;
/**
* 最高在线人数
*/
public static int MAX_ONLINE_COUNT;
public static final String SESSION_PERSON = "_PERSON_";
}