杰瑞科技汇

Servlet监听器如何实现与使用?

这是一个非常核心且重要的 Servlet API 组件,理解它对于构建健壮的 Web 应用至关重要。

Servlet监听器如何实现与使用?-图1
(图片来源网络,侵删)

什么是 Servlet 监听器?

一句话概括:

Servlet 监听器是 Servlet API 中的一类特殊接口,你可以实现这些接口来“监听” Servlet 容器(如 Tomcat、Jetty)的某些事件,当这些事件发生时,容器会自动调用你编写的监听器代码,让你可以在事件发生前后执行特定的逻辑。

打个比方:

想象一下 Servlet 容器是一个大楼。

Servlet监听器如何实现与使用?-图2
(图片来源网络,侵删)
  • ServletContextListener 就像一个大楼的“保安”,当大楼刚建成(启动)时,他会去检查消防设施、登记入住人员(初始化);当大楼要拆除了(关闭)时,他会去关掉水电、清理垃圾(销毁资源)。
  • HttpSessionListener 就像一个宴会厅的“迎宾员”,每当有一位新客人(Session 创建)进来,他会记录一下;每当有一位客人离开(Session 销毁),他会检查一下客人是否遗落了物品。

监听器的作用(为什么需要它?)

监听器主要用于在特定的生命周期节点执行初始化或清理工作,常见用途包括:

  1. 资源初始化与销毁:

    • 在 Web 应用启动时,初始化数据库连接池(如 Druid, HikariCP)。
    • 在 Web 应用关闭时,关闭连接池,释放所有资源。
    • 加载初始化配置文件到内存(如 application.properties)。
    • 启动一个后台线程(定时任务、消息队列消费者)。
  2. 统计信息:

    • 统计当前在线用户数(通过监听 HttpSession 的创建和销毁)。
    • 记录网站的访问日志(可以通过 ServletRequestListener 监听每次请求)。
  3. 数据预加载:

    Servlet监听器如何实现与使用?-图3
    (图片来源网络,侵删)
    • 在应用启动时,从数据库加载一些不常变化但被频繁使用的数据(如字典数据、配置信息)到 ServletContext 中,避免每次请求都查询数据库。

核心监听器接口详解

Servlet API 提供了多种监听器接口,它们可以分为三大类:作用域监听HTTP 会话监听,还有一个特殊的 ServletRequestListener

1 ServletContext 作用域监听

ServletContext 代表整个 Web 应用,一个应用只有一个 ServletContext 实例,它的生命周期与 Web 应用相同。

a) ServletContextListener

这是最常用、最重要的监听器。

  • 监听事件:
    • contextInitialized(ServletContextEvent sce): 当 Servlet 容器启动 Web 应用时调用(即 ServletContext 创建时)。
    • contextDestroyed(ServletContextEvent sce): 当 Servlet 容器关闭 Web 应用时调用(即 ServletContext 销毁时))。
  • 如何使用:
    1. 创建一个类,实现 ServletContextListener 接口。
    2. 实现两个方法 contextInitializedcontextDestroyed
    3. 在类上添加 @WebListener 注解,或者在 web.xml 中进行配置。

示例代码:

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
// 使用注解方式注册监听器
@WebListener
public class MyServletContextListener implements ServletContextListener {
    // 在 Web 应用启动时调用
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("===== Web 应用启动,ServletContext 初始化... =====");
        // 1. 初始化数据库连接池
        // DataSource dataSource = initDataSource();
        // 2. 加载配置文件
        // Properties props = loadProperties();
        // 3. 将数据存入 ServletContext,供整个应用共享
        // sce.getServletContext().setAttribute("dataSource", dataSource);
        System.out.println("===== 初始化完成! =====");
    }
    // 在 Web 应用关闭时调用
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("===== Web 应用关闭,ServletContext 销毁... =====");
        // 1. 关闭数据库连接池
        // DataSource dataSource = (DataSource) sce.getServletContext().getAttribute("dataSource");
        // dataSource.close();
        System.out.println("===== 资源释放完成! =====");
    }
}

b) ServletContextAttributeListener

  • 监听事件:
    • attributeAdded(ServletContextAttributeEvent scab): 当向 ServletContext 中添加属性时调用。
    • attributeRemoved(ServletContextAttributeEvent scab): 当从 ServletContext 中移除属性时调用。
    • attributeReplaced(ServletContextAttributeEvent scab): 当替换 ServletContext 中的属性时调用。

2 HttpSession 会话监听

HttpSession 代表一个用户与服务器之间的会话,每个用户会话都有一个独立的 HttpSession 实例。

a) HttpSessionListener

  • 监听事件:
    • sessionCreated(HttpSessionEvent se): 当创建一个新的 HttpSession 时调用(即用户第一次访问网站并创建了会话)。
    • sessionDestroyed(HttpSessionEvent se): 当销毁一个 HttpSession 时调用(如调用 session.invalidate()、会话超时等)。

示例代码(统计在线人数):

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class OnlineUserCounterListener implements HttpSessionListener {
    // 使用一个静态变量来记录在线人数
    private static int onlineCount = 0;
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        // 会话创建,人数加一
        onlineCount++;
        System.out.println("新用户上线,当前在线人数: " + onlineCount);
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        // 会话销毁,人数减一
        onlineCount--;
        System.out.println("用户下线,当前在线人数: " + onlineCount);
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }
}

b) HttpSessionAttributeListener

  • 监听事件:
    • attributeAdded(HttpSessionBindingEvent se): 当向 HttpSession 中添加属性时调用。
    • attributeRemoved(HttpSessionBindingEvent se): 当从 HttpSession 中移除属性时调用。
    • attributeReplaced(HttpSessionBindingEvent se): 当替换 HttpSession 中的属性时调用。

c) HttpSessionActivationListenerHttpSessionIdListener

  • HttpSessionActivationListener: 监听会话的序列化和反序列化(钝化和活化),用于分布式环境。
  • HttpSessionIdListener: 监听会话 ID 的变化。

3 ServletRequest 请求监听

ServletRequest 代表一个客户端请求。

a) ServletRequestListener

  • 监听事件:
    • requestInitialized(ServletRequestEvent sre): 当每次客户端请求到达时调用。
    • requestDestroyed(ServletRequestEvent sre): 当每次客户端请求处理完毕后调用。

用途: 可以用于记录请求日志、统一设置请求编码等。

b) ServletRequestAttributeListener

  • 监听事件:
    • attributeAdded(...), attributeRemoved(...), attributeReplaced(...): 监听 ServletRequest 属性的增删改。

如何注册监听器?

有两种方式:注解方式(推荐)web.xml 配置方式。

使用 @WebListener 注解(Java EE 6+ / Servlet 3.0+)

这是最简单、最现代的方式,只需在你实现的监听器类上添加 @WebListener 注解即可。

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class MySessionListener implements HttpSessionListener {
    // ...
}

web.xml 中配置(传统方式)

如果你的项目还在使用较老的 Servlet 规范,或者需要更复杂的配置,可以在 web.xml 中进行声明。

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 配置 ServletContextListener -->
    <listener>
        <listener-class>com.example.MyServletContextListener</listener-class>
    </listener>
    <!-- 配置 HttpSessionListener -->
    <listener>
        <listener-class>com.example.OnlineUserCounterListener</listener-class>
    </listener>
</web-app>

最佳实践与注意事项

  1. 监听器要轻量: 监听器中的代码应该尽可能快地执行,特别是 contextInitialized 方法,如果执行时间过长,会导致 Web 应用启动缓慢,耗时的操作(如加载大量数据)应该异步处理。
  2. 不要在监听器中处理业务逻辑: 监听器是生命周期管理工具,不是业务逻辑控制器,不要在监听器中直接处理用户的请求或复杂的业务流程。
  3. 异常处理: 监听器中的异常需要妥善处理。contextInitialized 方法抛出异常会导致整个 Web 应用启动失败。
  4. 监听器顺序: 如果有多个监听器,它们的执行顺序可以通过 @WebListenervalue 属性(在 web.xml 中通过 <listener> 的顺序)来控制,但在实际开发中,应尽量避免监听器之间有强依赖关系。
  5. 监听器的移除:contextDestroyed 方法中,一定要记得释放所有在 contextInitialized 中初始化的资源,防止内存泄漏。
监听器接口 监听对象 主要用途 核心方法
ServletContextListener ServletContext (整个应用) 应用启动/关闭时的初始化和销毁 contextInitialized, contextDestroyed
HttpSessionListener HttpSession (用户会话) 统计在线用户、会话生命周期管理 sessionCreated, sessionDestroyed
ServletRequestListener ServletRequest (单个请求) 记录请求日志、统一设置编码 requestInitialized, requestDestroyed
...AttributeListener 对应作用域的属性 监听作用域中属性的增删改 attributeAdded, attributeRemoved, attributeReplaced

掌握 Servlet 监听器是构建高质量 Java Web 应用的必备技能,它能帮助你更好地管理应用的生命周期和资源。

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