在 Java Servlet 规范中,Listener 是一种特殊的 Servlet 组件,用于监听和处理 Web 应用中的各种事件。这些事件可能涉及应用的生命周期、会话的创建与销毁、请求的开始与结束等。通过 Listener,开发者可以在特定事件发生时自动执行相应的逻辑,无需在业务代码中显式调用。
Java Servlet API 提供了多种 Listener 接口,允许开发者根据需要选择合适类型来监听特定事件
- ServletContextListener:监听整个 Web 应用的初始化和销毁事件
- HttpSessionListener:监听 Session 会话的创建和销毁事件
- ServletRequestListener:监听请求对象 Request 的创建和销毁事件
- ServletContextAttributeListener:监听 ServletContext 对象属性的改变事件
- HttpSessionAttributeListener:监听 HttpSession 对象属性的改变事件
- ServletRequestAttributeListener:监听 ServletRequest 对象属性的改变事件
Listener 的工作原理
Listener 工作的核心在于 Servlet 容器对特定事件的监听和触发机制。当这些事件发生时,容器自动调用相应的 Listener 方法,以执行预定义的逻辑。整个过程无需开发者手动触发 Listener
- 事件触发:当特定的事件发生时,如应用启动、会话创建等,Servlet 容器会检测并触发相应的事件
- Listener 响应:已注册的 Listener 会响应该事件,执行其定义的逻辑
- 生命周期管理:Servlet 容器负责管理 Listener 的生命周期,包括实例化、调用方法和销毁
plain
事件发生(如应用启动)
│
▼
Servlet 容器检测事件
│
▼
调用相应 Listener 的方法
│
▼
Listener 执行逻辑(如初始化资源)
创建和配置 Listener
和 Filter 的使用非常类似,使用 Listener,需要完成以下步骤:
- 穿件类实现相应的 Listener 接口
- 通过 @WebListener 注解或在 web.xml 文件中配置 Listener
实现 Listener 接口
创建一个 Java 类并实现所需的 Listener 接口。通常需要覆盖接口中的所有方法,以响应特定事件
java
package org.example;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener // 使用注解注册 Listener
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Web 应用启动时执行
System.out.println("Web 应用启动,初始化全局资源。");
// 例如,读取配置文件、初始化数据库连接池等
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Web 应用关闭时执行
System.out.println("Web 应用关闭,释放全局资源。");
// 例如,关闭数据库连接池、销毁缓存等
}
}
使用 web.xml 配置 Listener
尽管注解提供了便捷的配置方式,但在复杂场景下通过 web.xml 文件配置 Listener 会更清晰,在 web.xml 中,Listener 的加载顺序由 <listener>
标签的声明顺序决定
xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.example.AppContextListener</listener-class>
</listener>
<!-- 其 它配置 -->
</web-app>
ServletContextListener
ServletContext 表示整个 Web 应用程序的上下文环境,ServletContextListener 可以监听 ServletContext 对象的创建和销毁事件,实现对整个 Web 应用程序的初始化和清理等操作,典型用途:
- 初始化全局资源,如数据库连接池、配置文件读取等
- 释放全局资源,确保资源正确关闭,避免内存泄漏
接口提供了两个方法:
contextInitialized
:ServletContext 被初始化时容器调用,可以在此方法中进行 Web 应用程序的初始化操作,例如创建和配置数据库连接池、加载配置文件等contextDestroyed
:ServletContext 被销毁时容器调用,可以在此方法中进行 Web 应用程序的清理操作,例如关闭数据库连接池、释放系统资源等
java
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
ServletContext context = event.getServletContext();
// 在 ServletContext 中设置全局变量
context.setAttribute("dbURL", "jdbc:mysql://localhost/demo");
context.setAttribute("dbUser", "test");
context.setAttribute("dbPassword", "123456");
}
public void contextDestroyed(ServletContextEvent event) {
ServletContext context = event.getServletContext();
// 关闭数据库连接池等资源
// ...
}
}
使用 @WebListener 注解可以注册 Listener,同样也可以在 web.xml 中配置
xml
<listener>
<listener-class>com.example.MyServletContextListener</listener-class>
</listener>
这样容器在启动 Web 应用程序时会创建 MyServletContextListener 的实例,并在 ServletContext 初始化和销毁时分别调用 contextInitialized() 和 contextDestroyed() 方法
HttpSessionListener
HttpSession 对象表示用户会话,用于跟踪用户在应用程序中的活动。HttpSessionListener 可以监听 HttpSession 对象的创建和销毁事件,实现对用户会话的跟踪和管理等操作,典型用途:
- 跟踪活跃会话数,监控用户活动。
- 在会话创建时初始化会话属性。
- 在会话销毁时清理资源或记录日志。
接口提供了两个方法
sessionCreated
:HttpSession 对象被创建时容器调用,可以在此方法中记录用户访问信息、设置 session 的超时时间等sessionDestroyed
:HttpSession 对象被销毁时容器调用,可以在此方法中清理用户信息、释放资源等
java
public class MySessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = event.getSession();
// 记录用户访问信息
String sessionId = session.getId();
String ipAddress = session.getAttribute("userIPAddress");
System.out.println("Session created: " + sessionId + ", IP address: " + ipAddress);
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
// 清理用户信息
session.removeAttribute("userInfo");
// ...
}
}
ServletRequestListener
ServletRequest 对象表示客户端请求,封装了客户端请求的相关信息。ServletRequestListener 可以监听 ServletRequest 对象的创建和销毁事件,实现对请求参数的封装、请求时间的记录等操作,接口提供了两个方法
requestInitialized
:ServletRequest 对象被创建时容器调用,可以在此方法中记录请求时间、封装请求参数等requestDestroyed
:ServletRequest 对象被销毁时容器调用,可以在此方法中释放资源、记录请求处理结果等
java
public class MyRequestListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent event) {
ServletRequest request = event.getServletRequest();
// 记录请求时间
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
}
public void requestDestroyed(ServletRequestEvent event) {
ServletRequest request = event.getServletRequest();
// 计算请求处理时间
long startTime = (long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
long processingTime = endTime - startTime;
request.removeAttribute("startTime");
// 记录请求处理结果
System.out.println("Request processed in " + processingTime + " ms");
}
}
修改属性相关
除了常见的 ServletContextListener、HttpSessionListener、ServletRequestListener 外,还有一些 Listener 用于监听 ServletContextAttribute、HttpSessionAttribute、ServletRequestAttribute 等对象的属性修改事件
ServletContextAttributeListener
:用于监听 ServletContextAttribute 的添加、删除和替换事件。在 ServletContextAttribute 被添加、删除或替换时,会触发 attributeAdded()、attributeRemoved() 或 attributeReplaced() 方法HttpSessionAttributeListener
:用于监听 HttpSessionAttribute 的添加、删除和替换事件。在 HttpSessionAttribute 被添加、删除或替换时,会触发 attributeAdded()、attributeRemoved() 或 attributeReplaced() 方法ServletRequestAttributeListener
:用于监听 ServletRequestAttribute 的添加、删除和替换事件。在 ServletRequestAttribute 被添加、删除或替换时,会触发 attributeAdded()、attributeRemoved() 或 attributeReplaced() 方法
java
public class MyServletContextAttributeListener implements ServletContextAttributeListener {
public void attributeAdded(ServletContextAttributeEvent event) {
String attributeName = event.getName();
Object attributeValue = event.getValue();
System.out.println("Attribute added: " + attributeName + "=" + attributeValue);
}
public void attributeRemoved(ServletContextAttributeEvent event) {
String attributeName = event.getName();
Object attributeValue = event.getValue();
System.out.println("Attribute removed: " + attributeName + "=" + attributeValue);
}
public void attributeReplaced(ServletContextAttributeEvent event) {
String attributeName = event.getName();
Object oldAttributeValue = event.getValue();
Object newAttributeValue = event.getServletContext().getAttribute(attributeName);
System.out.println("Attribute replaced: " + attributeName + "=" + oldAttributeValue + "->" + newAttributeValue);
}
}