杰瑞科技汇

java session 监听

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 对象从创建到销毁的整个过程。

  1. HttpSessionListener

    • 作用:监听 Session 的创建和销毁。
    • 方法
      • sessionCreated(HttpSessionEvent se): 当一个 Session 对象被创建时调用。
      • sessionDestroyed(HttpSessionEvent se): 当一个 Session 对象被销毁时调用。
  2. HttpSessionAttributeListener

    • 作用:监听 Session 中属性的变化(增、删、改)。
    • 方法
      • attributeAdded(HttpSessionBindingEvent se): 当向 Session 中添加一个属性时调用。
      • attributeRemoved(HttpSessionBindingEvent se): 当从 Session 中移除一个属性时调用。
      • attributeReplaced(HttpSessionBindingEvent se): 当 Session 中某个属性被替换为新值时调用。

B. 绑定监听器

这类监听器稍微复杂一些,它关注 Java 对象与 Session 之间的“绑定”和“解绑”关系。

  1. HttpSessionBindingListener

    • 作用:监听单个对象自己与 Session 的绑定和解绑过程。
    • 特点:这个接口不需要在 web.xml 中注册,实现该接口的类,其实例在被放入 Session 或从 Session 中移除时,会自动调用其自身的 valueBoundvalueUnbound 方法。
    • 方法
      • valueBound(HttpSessionBindingEvent event): 当对象被绑定到 Session 时调用。
      • valueUnbound(HttpSessionBindingEvent event): 当对象从 Session 中解绑时调用。
  2. 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");
%>

运行流程:

  1. 启动 Tomcat 服务器。
  2. 在浏览器中打开第一个窗口,访问 index.jsp,观察 Tomcat 控制台,会打印 "Session 创建,当前在线人数: 1",页面上显示 "当前在线人数: 1"。
  3. 在浏览器中打开第二个窗口(无痕模式或不同浏览器),再次访问 index.jsp,控制台会打印 "Session 创建,当前在线人数: 2",页面上显示 "当前在线人数: 2"。
  4. 关闭第一个窗口,控制台会打印 "Session 销毁,当前在线人数: 1"。
  5. 点击第二个窗口的“退出登录”链接,控制台会打印 "Session 销毁,当前在线人数: 0"。

Session 监听器的典型应用场景

  1. 统计在线用户数:如上例所示。
  2. 用户行为分析:在 attributeAddedattributeReplaced 方法中记录用户的关键操作,如登录、添加商品到购物车等。
  3. 统一资源管理:Session 中存放了数据库连接、文件流等需要手动释放的资源,可以在 sessionDestroyed 方法中统一关闭和释放它们,防止资源泄漏。
  4. 单点登录控制:当用户在 A 系统登录后,可以通过监听器在 Session 中设置一个标记,当用户尝试在 B 系统登录时,检查这个标记,如果已存在,则可以实现单点登出或拒绝登录。
  5. 处理 Session 钝化/活化:Session 中存放了不能被序列化的对象(如数据库连接),可以在 sessionWillPassivate 方法中将其移除,在 sessionDidActivate 方法中重新创建或获取。

注意事项

  • 性能影响:监听器中的代码应尽量简洁、高效,避免执行耗时操作(如数据库查询、网络请求),否则会影响整个 Web 应用的性能。

  • 线程安全HttpSessionListenersessionCreatedsessionDestroyed 方法可能被多个线程并发调用(如果服务器是多线程的),如果共享了可变状态(如 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 中的声明顺序或类名字母顺序决定(取决于容器),如果顺序很重要,可以使用 @WebListenervalue 属性(如 @WebListener(value="first"))或在 web.xml 中明确指定。

监听器接口 监听事件 主要用途
HttpSessionListener Session 创建、销毁 统计在线人数、初始化/销毁 Session 级别资源
HttpSessionAttributeListener Session 属性添加、移除、替换 记录用户行为、审计日志
HttpSessionBindingListener 对象与 Session 绑定、解绑 对象自身感知其在 Session 中的生命周期
HttpSessionActivationListener Session 钝化、活化 处理不能被序列化的资源

掌握 Session 监听器是 Java Web 开发中一项非常重要的技能,能够帮助你构建更健壮、更智能的 Web 应用。

分享:
扫描分享到社交APP
上一篇
下一篇