首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Web Listener 完整指南:Servlet 監聽器八大介面、源碼解析與實戰應用

     編輯:Java 相關知識

核心概念:觀察者模式在 Servlet 中的實踐

Listener 本質是 觀察者模式:Servlet 容器 (主題/被觀察者) 監聽生命週期事件,通過回調通知已註冊的 Listener (觀察者)。

八大標準監聽器介面 (Servlet 3.0+)

分類介面監聽事件典型應用
ServletContextServletContextListener應用啟動/銷毀初始化緩存、加載配置、啟動定時任務、關閉連接池
ServletContextAttributeListener屬性增/刪/改監控全局屬性變更、審計日誌
HttpSessionHttpSessionListenerSession 創建/銷毀在線用戶統計、Session 持久化
HttpSessionActivationListener鈍化/活化分布式 Session 遷移、序列化控制
HttpSessionAttributeListener屬性增/刪/改購物車變更、權限動態刷新
HttpSessionBindingListener綁定/解綁 (值本身實現)對象感知自身 Session 狀態、自動清理資源
ServletRequestServletRequestListener請求初始化/銷毀請求耗時統計、ThreadLocal 清理、Open Session In View
ServletRequestAttributeListener屬性增/刪/改請求級數據追蹤、調試

核心介面源碼解析

事件基類體系

EventObject (java.util)
  └── ServletContextEvent
        ├── ServletContextAttributeEvent (屬性名/值)
        └── HttpSessionEvent
              ├── HttpSessionBindingEvent (屬性名/值)
              └── ServletRequestEvent
                    └── ServletRequestAttributeEvent (屬性名/值)

關鍵介面定義

// 應用級
public interface ServletContextListener extends EventListener {
    void contextInitialized(ServletContextEvent sce);  // 啟動完成
    void contextDestroyed(ServletContextEvent sce);    // 關閉前 (所有 Servlet/Filter destroy 後)
}

public interface ServletContextAttributeListener extends EventListener {
    void attributeAdded(ServletContextAttributeEvent scae);
    void attributeRemoved(ServletContextAttributeEvent scae);
    void attributeReplaced(ServletContextAttributeEvent scae); // 獲取舊值
}

// Session 級
public interface HttpSessionListener extends EventListener {
    void sessionCreated(HttpSessionEvent se);
    void sessionDestroyed(HttpSessionEvent se);
}

public interface HttpSessionBindingListener extends EventListener {
    void valueBound(HttpSessionBindingEvent event);   // session.setAttribute(this)
    void valueUnbound(HttpSessionBindingEvent event); // remove/invalidate
}

// 請求級
public interface ServletRequestListener extends EventListener {
    void requestInitialized(ServletRequestEvent sre);  // 進入第一個 Filter/Servlet 前
    void requestDestroyed(ServletRequestEvent sre);    // 離開最後一個 Filter/Servlet 後
}

實戰示例

1. 應用啟動初始化 (ServletContextListener)

@WebListener
public class AppInitializer implements ServletContextListener {
    
    private ScheduledExecutorService scheduler;
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        
        // 加載全局配置
        String configPath = ctx.getInitParameter("config.path");
        GlobalConfig.load(configPath);
        
        // 初始化緩存
        CacheManager.init(ctx);
        
        // 啟動定時任務
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(
            () -> CacheManager.cleanupExpired(), 
            1, 1, TimeUnit.HOURS
        );
        
        ctx.log("應用初始化完成");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        if (scheduler != null) scheduler.shutdown();
        CacheManager.shutdown();
        DataSourceUtils.closeAll();
        sce.getServletContext().log("應用優雅關閉");
    }
}

2. 在線用戶統計 (HttpSessionListener)

@WebListener
public class OnlineCounter implements HttpSessionListener {
    
    private static final AtomicInteger ONLINE_COUNT = new AtomicInteger(0);
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        ONLINE_COUNT.incrementAndGet();
        se.getSession().getServletContext().log("在線用戶: " + ONLINE_COUNT.get());
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        ONLINE_COUNT.decrementAndGet();
    }
    
    public static int getOnlineCount() { return ONLINE_COUNT.get(); }
}

3. 購物車自動持久化 (HttpSessionBindingListener)

public class ShoppingCart implements HttpSessionBindingListener, Serializable {
    
    private Map items = new ConcurrentHashMap<>();
    private Long userId;
    
    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        // 放入 Session 時:從資料庫恢復
        HttpSession session = event.getSession();
        this.userId = (Long) session.getAttribute("userId");
        if (userId != null) {
            CartDAO.load(userId).ifPresent(cart -> this.items.putAll(cart.getItems()));
        }
    }
    
    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        // 移出 Session 時:持久化到資料庫
        if (userId != null && !items.isEmpty()) {
            CartDAO.save(userId, this);
        }
    }
    
    // 業務方法...
}

4. 請求耗時監控 (ServletRequestListener)

@WebListener
public class RequestTimer implements ServletRequestListener {
    
    private static final ThreadLocal START_TIME = new ThreadLocal<>();
    
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        START_TIME.set(System.nanoTime());
    }
    
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        long start = START_TIME.get();
        if (start != null) {
            long durationMs = (System.nanoTime() - start) / 1_000_000;
            HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
            
            // 記錄慢請求
            if (durationMs > 1000) {
                log.warn("慢請求: {} {} - {}ms", req.getMethod(), req.getRequestURI(), durationMs);
            }
            
            // 可接入 Metrics 系統 (Micrometer/Prometheus)
            Metrics.timer("http.request.duration", "uri", req.getRequestURI())
                   .record(durationMs, TimeUnit.MILLISECONDS);
        }
        START_TIME.remove(); // 防洩漏
    }
}

註冊方式對比

Listener vs Filter vs Servlet

方式優點缺點推薦場景
web.xml <listener>標準、無框架依賴配置分散、不支持條件註冊傳統項目、共享庫
@WebListener 註解零配置、類旁可見掃描啟動稍慢、無法動態禁用Spring Boot、現代項目
動態註冊 ServletContext.addListener()運行時決定、條件化代碼較繁瑣插件化、多租戶、功能開關
組件生命週期核心職責典型用例
Listener應用/Session/請求級事件監聽、回調、副作用初始化、統計、清理、審計
Filter請求處理鏈中預處理/後處理、攔截編碼、認證、日誌、壓縮、XSS
Servlet請求處理終點業務邏輯、響應生成Controller、REST API、JSP

最佳實踐總結

  1. 單一職責:一個 Listener 只做一類事
  2. 異步處理:耗時操作 (DB、網絡) 用線程池/異步 Servlet
  3. 異常隔離:Listener 異常不應影響主流程,捕獲並記錄
  4. 資源清理:contextDestroyed / requestDestroyed 必須清理 ThreadLocal、連接池、線程池
  5. 順序控制:多個 Listener 無順序保證,有依賴請合併或用初始化參數協調

總結

Servlet Listener 是 Web 應用的「神經系統」,負責感知生命週期事件並觸發業務邊界動作。掌握八大介面、事件傳遞機制、註冊方式與實戰模式,能讓你優雅地處理初始化、統計、緩存、持久化等橫切關注點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved