Session 监听是 Servlet 规范中一个非常重要的特性,它允许你通过监听器来捕获 Session 对象在整个生命周期中的关键事件,从而实现一些高级功能,比如统计在线人数、记录用户行为、统一管理 Session 资源等。
什么是 Session?
在 Web 开发中,HTTP 协议本身是无状态的(Stateless),这意味着服务器无法区分两个连续的请求是否来自同一个用户,为了解决这个问题,Session 技术应运而生。
- Session 是服务器端为每个客户端(通常通过浏览器)创建的一个对象,用于在多个请求之间保存用户的状态信息。
- 服务器会为每个 Session 分配一个唯一的 ID,称为
Session ID。 - 服务器通过
Session ID来识别和获取对应的 Session 对象,这个 ID 通常通过 Cookie 发送给客户端,并在后续请求中带回服务器。
什么是 Session 监听器?
Session 监听器是一个实现了特定接口的 Java 类,当 Session 对象被创建、销毁,或者当 Session 中的属性被添加、删除、替换时,容器(如 Tomcat)会自动调用监听器中相应的方法。
这允许你在这些关键节点执行自定义的逻辑,而无需修改业务代码。
Session 监听器的核心接口
Servlet 规范提供了与 Session 相关的监听器接口,主要分为两大类:
A. 生命周期监听器
这类监听器关注 Session 对象从创建到销毁的整个过程。
-
HttpSessionListener- 作用:监听 Session 的创建和销毁。
- 方法:
sessionCreated(HttpSessionEvent se): 当一个 Session 对象被创建时调用。sessionDestroyed(HttpSessionEvent se): 当一个 Session 对象被销毁时调用。
-
HttpSessionAttributeListener- 作用:监听 Session 中属性的变化(增、删、改)。
- 方法:
attributeAdded(HttpSessionBindingEvent se): 当向 Session 中添加一个属性时调用。attributeRemoved(HttpSessionBindingEvent se): 当从 Session 中移除一个属性时调用。attributeReplaced(HttpSessionBindingEvent se): 当 Session 中某个属性被替换为新值时调用。
B. 绑定监听器
这类监听器稍微复杂一些,它关注 Java 对象与 Session 之间的“绑定”和“解绑”关系。
-
HttpSessionBindingListener- 作用:监听单个对象自己与 Session 的绑定和解绑过程。
- 特点:这个接口不需要在
web.xml中注册,实现该接口的类,其实例在被放入 Session 或从 Session 中移除时,会自动调用其自身的valueBound和valueUnbound方法。 - 方法:
valueBound(HttpSessionBindingEvent event): 当对象被绑定到 Session 时调用。valueUnbound(HttpSessionBindingEvent event): 当对象从 Session 中解绑时调用。
-
HttpSessionActivationListener- 作用:监听 Session 的序列化和反序列化过程(也称为“活化”和“钝化”)。
- 使用场景:当服务器内存紧张时,可能会将一些不常用的 Session 对象序列化到硬盘上(钝化, Passivate),以释放内存,当客户端再次请求时,服务器会将这些 Session 对象从硬盘反序列化回内存(活化, Activate)。
- 方法:
sessionWillPassivate(HttpSessionEvent se): 当 Session 即将被钝化(序列化)到硬盘时调用。sessionDidActivate(HttpSessionEvent se): 当 Session 已经被活化(反序列化)回内存时调用。
如何实现和使用 Session 监听器?
我们通过一个最常用的例子来演示:统计当前在线人数。
这个功能需要用到 HttpSessionListener。
步骤 1:创建监听器类
创建一个 Java 类,实现 HttpSessionListener 接口。
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* 监听 Session 的创建和销毁,用于统计在线人数
*/
@WebListener // 使用注解方式注册,更现代简便
public class OnlineCountListener implements HttpSessionListener {
// 在线人数计数器
private static int count = 0;
/**
* 当 Session 被创建时,此方法被调用
* @param se Session 事件
*/
@Override
public void sessionCreated(HttpSessionEvent se) {
// 计数器加一
count++;
System.out.println("Session 创建,当前在线人数: " + count);
// 可以将人数存入 ServletContext,方便在 JSP 或其他 Servlet 中获取
se.getSession().getServletContext().setAttribute("onlineCount", count);
}
/**
* 当 Session 被销毁时,此方法被调用
* @param se Session 事件
*/
@Override
public void sessionDestroyed(HttpSessionEvent se) {
// 计数器减一
count--;
System.out.println("Session 销毁,当前在线人数: " + count);
// 更新 ServletContext 中的值
se.getSession().getServletContext().setAttribute("onlineCount", count);
}
}
步骤 2:注册监听器
有两种方式注册监听器:
使用注解(推荐)
在监听器类上使用 @WebListener 注解,如上面的代码所示,Servlet 3.0+ 版本支持这种方式,非常方便。
在 web.xml 中配置(传统方式)
如果你的项目还在使用较老的 Servlet 版本,或者需要更复杂的配置,可以在 web.xml 文件中添加以下内容:
<web-app ...>
...
<listener>
<listener-class>com.yourpackage.OnlineCountListener</listener-class>
</listener>
...
</web-app>
步骤 3:测试监听器效果
创建一个简单的 JSP 页面来显示在线人数。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="javax.servlet.http.HttpSession" %>
<html>
<head>在线人数统计</title>
</head>
<body>
<h1>欢迎访问!</h1>
<p>当前在线人数: ${applicationScope.onlineCount}</p>
<%
// 为了让 Session 不会立即过期,可以在这里设置一个超时时间(单位:秒)
// 默认是 30 分钟
session.setMaxInactiveInterval(60); // 设置为 60 秒
%>
<a href="logout.jsp">退出登录(注销 Session)</a>
</body>
</html>
再创建一个 logout.jsp 用于手动销毁 Session:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
// 销毁当前 Session
session.invalidate();
// 重定向到主页
response.sendRedirect("index.jsp");
%>
运行流程:
- 启动 Tomcat 服务器。
- 在浏览器中打开第一个窗口,访问
index.jsp,观察 Tomcat 控制台,会打印 "Session 创建,当前在线人数: 1",页面上显示 "当前在线人数: 1"。 - 在浏览器中打开第二个窗口(无痕模式或不同浏览器),再次访问
index.jsp,控制台会打印 "Session 创建,当前在线人数: 2",页面上显示 "当前在线人数: 2"。 - 关闭第一个窗口,控制台会打印 "Session 销毁,当前在线人数: 1"。
- 点击第二个窗口的“退出登录”链接,控制台会打印 "Session 销毁,当前在线人数: 0"。
Session 监听器的典型应用场景
- 统计在线用户数:如上例所示。
- 用户行为分析:在
attributeAdded或attributeReplaced方法中记录用户的关键操作,如登录、添加商品到购物车等。 - 统一资源管理:Session 中存放了数据库连接、文件流等需要手动释放的资源,可以在
sessionDestroyed方法中统一关闭和释放它们,防止资源泄漏。 - 单点登录控制:当用户在 A 系统登录后,可以通过监听器在 Session 中设置一个标记,当用户尝试在 B 系统登录时,检查这个标记,如果已存在,则可以实现单点登出或拒绝登录。
- 处理 Session 钝化/活化:Session 中存放了不能被序列化的对象(如数据库连接),可以在
sessionWillPassivate方法中将其移除,在sessionDidActivate方法中重新创建或获取。
注意事项
-
性能影响:监听器中的代码应尽量简洁、高效,避免执行耗时操作(如数据库查询、网络请求),否则会影响整个 Web 应用的性能。
-
线程安全:
HttpSessionListener的sessionCreated和sessionDestroyed方法可能被多个线程并发调用(如果服务器是多线程的),如果共享了可变状态(如count计数器),需要考虑线程安全问题,在上面的例子中,++count和--count并不是原子操作,在高并发场景下可能会出错,可以使用AtomicInteger来保证原子性:import java.util.concurrent.atomic.AtomicInteger; private static AtomicInteger count = new AtomicInteger(0); @Override public void sessionCreated(HttpSessionEvent se) { count.incrementAndGet(); // ... } @Override public void sessionDestroyed(HttpSessionEvent se) { count.decrementAndGet(); // ... } -
监听器顺序:如果配置了多个监听器,它们的执行顺序由
web.xml中的声明顺序或类名字母顺序决定(取决于容器),如果顺序很重要,可以使用@WebListener的value属性(如@WebListener(value="first"))或在web.xml中明确指定。
| 监听器接口 | 监听事件 | 主要用途 |
|---|---|---|
HttpSessionListener |
Session 创建、销毁 | 统计在线人数、初始化/销毁 Session 级别资源 |
HttpSessionAttributeListener |
Session 属性添加、移除、替换 | 记录用户行为、审计日志 |
HttpSessionBindingListener |
对象与 Session 绑定、解绑 | 对象自身感知其在 Session 中的生命周期 |
HttpSessionActivationListener |
Session 钝化、活化 | 处理不能被序列化的资源 |
掌握 Session 监听器是 Java Web 开发中一项非常重要的技能,能够帮助你构建更健壮、更智能的 Web 应用。
