Skip to content

Servlet监听器详解

监听器概述

Servlet监听器(Listener)是Java Web应用中用于监听和响应Web应用生命周期事件以及属性变化事件的组件。监听器允许开发者在特定事件发生时执行自定义代码,实现对Web应用的监控和控制。

监听器的作用

  • 生命周期管理:监听Web应用、Session和Request的创建和销毁
  • 属性变更监听:监控应用范围、会话范围和请求范围内属性的变化
  • 会话管理:监控会话中对象的绑定和解绑
  • 异步处理:监听异步处理的开始和结束
  • 请求状态监控:监控请求的生命周期

监听器的类型

根据监听的事件类型,Servlet监听器主要分为以下几类:

  1. ServletContext监听器
  2. HttpSession监听器
  3. ServletRequest监听器
  4. 属性变更监听器
  5. Session相关的对象绑定监听器

ServletContext监听器

ServletContextListener

作用:监听ServletContext(应用上下文)的创建和销毁。

主要方法

java
public interface ServletContextListener extends EventListener {
    // 当Web应用启动时调用
    void contextInitialized(ServletContextEvent sce);
    
    // 当Web应用关闭时调用
    void contextDestroyed(ServletContextEvent sce);
}

使用示例

java
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import java.util.logging.Logger;

@WebListener
public class AppContextListener implements ServletContextListener {
    private static final Logger logger = Logger.getLogger(AppContextListener.class.getName());
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        logger.info("Web应用初始化开始");
        
        // 应用初始化操作
        // 1. 加载配置文件
        String configPath = context.getInitParameter("configPath");
        logger.info("配置文件路径: " + configPath);
        
        // 2. 初始化数据库连接池
        // initDatabasePool();
        
        // 3. 加载全局缓存数据
        // loadCacheData();
        
        // 4. 设置全局属性
        context.setAttribute("appStartTime", System.currentTimeMillis());
        
        logger.info("Web应用初始化完成");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        logger.info("Web应用销毁开始");
        
        // 应用销毁前清理操作
        // 1. 关闭数据库连接池
        // closeDatabasePool();
        
        // 2. 释放其他资源
        // releaseResources();
        
        logger.info("Web应用销毁完成");
    }
}

ServletContextAttributeListener

作用:监听ServletContext范围内属性的添加、删除和替换。

主要方法

java
public interface ServletContextAttributeListener extends EventListener {
    // 当属性添加到ServletContext时调用
    void attributeAdded(ServletContextAttributeEvent scae);
    
    // 当属性从ServletContext移除时调用
    void attributeRemoved(ServletContextAttributeEvent scae);
    
    // 当ServletContext中的属性被替换时调用
    void attributeReplaced(ServletContextAttributeEvent scae);
}

使用示例

java
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import java.util.logging.Logger;

@WebListener
public class AppContextAttributeListener implements ServletContextAttributeListener {
    private static final Logger logger = Logger.getLogger(AppContextAttributeListener.class.getName());
    
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object value = scae.getValue();
        logger.info("应用属性添加: " + name + " = " + value);
    }
    
    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object value = scae.getValue();
        logger.info("应用属性移除: " + name + " = " + value);
    }
    
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        String name = scae.getName();
        Object oldValue = scae.getValue();
        ServletContext context = scae.getServletContext();
        Object newValue = context.getAttribute(name);
        logger.info("应用属性替换: " + name + " 从 " + oldValue + " 变为 " + newValue);
    }
}

HttpSession监听器

HttpSessionListener

作用:监听HTTP会话的创建和销毁。

主要方法

java
public interface HttpSessionListener extends EventListener {
    // 当会话创建时调用
    void sessionCreated(HttpSessionEvent se);
    
    // 当会话销毁时调用
    void sessionDestroyed(HttpSessionEvent se);
}

使用示例

java
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

@WebListener
public class SessionListener implements HttpSessionListener {
    private static final Logger logger = Logger.getLogger(SessionListener.class.getName());
    // 在线用户计数器
    private static final AtomicInteger activeUsers = new AtomicInteger(0);
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        String sessionId = session.getId();
        logger.info("会话创建: " + sessionId);
        
        // 增加在线用户数
        int count = activeUsers.incrementAndGet();
        logger.info("当前在线用户数: " + count);
        
        // 设置会话超时时间(单位:秒)
        session.setMaxInactiveInterval(1800); // 30分钟
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        String sessionId = session.getId();
        logger.info("会话销毁: " + sessionId);
        
        // 减少在线用户数
        int count = activeUsers.decrementAndGet();
        logger.info("当前在线用户数: " + count);
        
        // 清理会话中的资源
        // 例如:移除绑定到会话的临时文件等
    }
    
    // 获取当前在线用户数
    public static int getActiveUsersCount() {
        return activeUsers.get();
    }
}

HttpSessionAttributeListener

作用:监听HTTP会话范围内属性的添加、删除和替换。

主要方法

java
public interface HttpSessionAttributeListener extends EventListener {
    // 当属性添加到会话时调用
    void attributeAdded(HttpSessionBindingEvent se);
    
    // 当属性从会话移除时调用
    void attributeRemoved(HttpSessionBindingEvent se);
    
    // 当会话中的属性被替换时调用
    void attributeReplaced(HttpSessionBindingEvent se);
}

使用示例

java
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.logging.Logger;

@WebListener
public class SessionAttributeListener implements HttpSessionAttributeListener {
    private static final Logger logger = Logger.getLogger(SessionAttributeListener.class.getName());
    
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        String sessionId = se.getSession().getId();
        logger.info("会话属性添加[" + sessionId + "]: " + name + " = " + value);
        
        // 特别处理用户登录
        if ("user".equals(name)) {
            logger.info("用户登录: " + value);
            // 可以记录登录日志、更新登录状态等
        }
    }
    
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object value = se.getValue();
        String sessionId = se.getSession().getId();
        logger.info("会话属性移除[" + sessionId + "]: " + name + " = " + value);
        
        // 特别处理用户登出
        if ("user".equals(name)) {
            logger.info("用户登出: " + value);
            // 可以记录登出日志、清理用户相关资源等
        }
    }
    
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        String name = se.getName();
        Object oldValue = se.getValue();
        Object newValue = se.getSession().getAttribute(name);
        String sessionId = se.getSession().getId();
        logger.info("会话属性替换[" + sessionId + "]: " + name + " 从 " + oldValue + " 变为 " + newValue);
    }
}

ServletRequest监听器

ServletRequestListener

作用:监听HTTP请求的创建和销毁。

主要方法

java
public interface ServletRequestListener extends EventListener {
    // 当请求创建时调用
    void requestInitialized(ServletRequestEvent sre);
    
    // 当请求销毁时调用
    void requestDestroyed(ServletRequestEvent sre);
}

使用示例

java
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import java.util.logging.Logger;

@WebListener
public class RequestListener implements ServletRequestListener {
    private static final Logger logger = Logger.getLogger(RequestListener.class.getName());
    
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        String requestURI = request.getRequestURI();
        String clientIP = request.getRemoteAddr();
        String sessionId = request.getSession().getId();
        
        logger.info("请求初始化: " + requestURI + " from " + clientIP + " (session: " + sessionId + ")");
        
        // 记录请求开始时间,用于计算请求处理时间
        long startTime = System.currentTimeMillis();
        request.setAttribute("requestStartTime", startTime);
    }
    
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
        String requestURI = request.getRequestURI();
        
        // 计算请求处理时间
        Long startTime = (Long) request.getAttribute("requestStartTime");
        if (startTime != null) {
            long processTime = System.currentTimeMillis() - startTime;
            logger.info("请求销毁: " + requestURI + " 处理时间: " + processTime + "ms");
            
            // 如果处理时间过长,可以记录警告
            if (processTime > 1000) { // 超过1秒
                logger.warning("请求处理时间过长: " + requestURI + " 耗时: " + processTime + "ms");
            }
        } else {
            logger.info("请求销毁: " + requestURI);
        }
        
        // 清理请求中的临时资源
        request.removeAttribute("requestStartTime");
    }
}

ServletRequestAttributeListener

作用:监听HTTP请求范围内属性的添加、删除和替换。

主要方法

java
public interface ServletRequestAttributeListener extends EventListener {
    // 当属性添加到请求时调用
    void attributeAdded(ServletRequestAttributeEvent srae);
    
    // 当属性从请求移除时调用
    void attributeRemoved(ServletRequestAttributeEvent srae);
    
    // 当请求中的属性被替换时调用
    void attributeReplaced(ServletRequestAttributeEvent srae);
}

使用示例

java
import javax.servlet.*;
import javax.servlet.annotation.WebListener;
import java.util.logging.Logger;

@WebListener
public class RequestAttributeListener implements ServletRequestAttributeListener {
    private static final Logger logger = Logger.getLogger(RequestAttributeListener.class.getName());
    
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object value = srae.getValue();
        logger.info("请求属性添加: " + name + " = " + value);
    }
    
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object value = srae.getValue();
        logger.info("请求属性移除: " + name + " = " + value);
    }
    
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        String name = srae.getName();
        Object oldValue = srae.getValue();
        ServletRequest request = srae.getServletRequest();
        Object newValue = request.getAttribute(name);
        logger.info("请求属性替换: " + name + " 从 " + oldValue + " 变为 " + newValue);
    }
}

Session对象绑定监听器

HttpSessionBindingListener

作用:监听对象被绑定到会话或从会话中解绑的事件。

与其他监听器不同,HttpSessionBindingListener是由被绑定的对象实现的,而不是由单独的监听器类实现。这样,当对象被添加到会话或从会话中移除时,对象自身就能感知到。

主要方法

java
public interface HttpSessionBindingListener extends EventListener {
    // 当对象被绑定到会话时调用
    void valueBound(HttpSessionBindingEvent event);
    
    // 当对象从会话中解绑时调用
    void valueUnbound(HttpSessionBindingEvent event);
}

使用示例

java
import javax.servlet.http.*;
import java.util.logging.Logger;

// 用户类实现HttpSessionBindingListener接口
public class User implements HttpSessionBindingListener {
    private static final Logger logger = Logger.getLogger(User.class.getName());
    
    private String username;
    private String email;
    private String lastLoginIP;
    
    // 构造方法、getter和setter方法
    public User(String username, String email, String lastLoginIP) {
        this.username = username;
        this.email = email;
        this.lastLoginIP = lastLoginIP;
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String username) {
        this.username = username;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public String getLastLoginIP() {
        return lastLoginIP;
    }
    
    public void setLastLoginIP(String lastLoginIP) {
        this.lastLoginIP = lastLoginIP;
    }
    
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        String sessionId = session.getId();
        logger.info("用户" + username + "被绑定到会话" + sessionId);
        
        // 执行用户登录后的操作
        // 例如:更新用户登录状态、记录登录日志等
        // updateLoginStatus(true);
        // logUserLogin();
    }
    
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        HttpSession session = event.getSession();
        String sessionId = session.getId();
        logger.info("用户" + username + "从会话" + sessionId + "中解绑");
        
        // 执行用户登出后的操作
        // 例如:更新用户登出状态、清理用户相关资源等
        // updateLoginStatus(false);
        // cleanupUserResources();
    }
    
    @Override
    public String toString() {
        return "User{username='" + username + "', email='" + email + "'}";
    }
}

// 在Servlet中使用
// User user = new User("admin", "admin@example.com", request.getRemoteAddr());
// session.setAttribute("user", user); // 这里会触发valueBound方法
// session.removeAttribute("user"); // 这里会触发valueUnbound方法

HttpSessionActivationListener

作用:监听会话的钝化(序列化到磁盘)和活化(从磁盘加载)事件。

当Web容器需要释放内存时,可能会将会话序列化到磁盘(钝化);当需要再次使用会话时,会从磁盘加载会话(活化)。

主要方法

java
public interface HttpSessionActivationListener extends EventListener {
    // 当会话被活化(从磁盘加载)时调用
    void sessionDidActivate(HttpSessionEvent se);
    
    // 当会话被钝化(序列化到磁盘)时调用
    void sessionWillPassivate(HttpSessionEvent se);
}

使用示例

java
import javax.servlet.http.*;
import java.io.Serializable;
import java.util.logging.Logger;

// 需要实现Serializable接口,才能被序列化
public class ShoppingCart implements HttpSessionActivationListener, Serializable {
    private static final long serialVersionUID = 1L;
    private static final Logger logger = Logger.getLogger(ShoppingCart.class.getName());
    
    private String cartId;
    private java.util.Map<String, Integer> items = new java.util.HashMap<>();
    
    // 构造方法、getter和setter方法
    public ShoppingCart(String cartId) {
        this.cartId = cartId;
    }
    
    public void addItem(String productId, int quantity) {
        items.put(productId, quantity);
    }
    
    public void removeItem(String productId) {
        items.remove(productId);
    }
    
    public int getTotalItems() {
        return items.size();
    }
    
    @Override
    public void sessionDidActivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        logger.info("购物车" + cartId + "被活化,会话ID: " + session.getId());
        
        // 会话活化后的初始化操作
        // 例如:重新加载关联的数据库数据等
        // reloadDataFromDatabase();
    }
    
    @Override
    public void sessionWillPassivate(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        logger.info("购物车" + cartId + "将被钝化,会话ID: " + session.getId());
        
        // 会话钝化前的清理操作
        // 例如:释放不能序列化的资源等
        // releaseNonSerializableResources();
    }
    
    @Override
    public String toString() {
        return "ShoppingCart{cartId='" + cartId + "', items=