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

JavaWeb Listener 監聽器完整指南:從原理到 Servlet 規範實戰

     編輯:Java 相關知識

一、監聽器核心概念

定義:專門監聽其他對象事件或狀態變化,並即時響應處理的對象。本質是實現特定介面的普通 Java 類。

事件處理模型三要素:

  1. 事件源:被監聽的對象(如 Window、ServletContext)
  2. 事件對象:封裝事件源與上下文信息(如 WindowEvent、ServletContextEvent)
  3. 事件監聽器:實現監聽介面,處理具體邏輯

二、手寫觀察者模式:設計可被監聽的對象

遵循嚴格事件模型設計 Person 類:


// 事件對象:封裝事件源
class Event {
    private final Person source;
    Event(Person source) { this.source = source; }
    Person getSource() { return source; }
}

// 監聽器介面:定義回調契約
interface PersonListener {
    void doEat(Event e);
    void doRun(Event e);
}

// 事件源:管理監聽器註冊與事件分發
class Person {
    private PersonListener listener;
    
    public void registerListener(PersonListener l) { this.listener = l; }
    
    public void eat() {
        if (listener != null) listener.doEat(new Event(this));
    }
    public void run() {
        if (listener != null) listener.doRun(new Event(this));
    }
}

// 使用
Person p = new Person();
p.registerListener(new PersonListener() {
    public void doEat(Event e) { System.out.println(e.getSource() + " 在吃飯"); }
    public void doRun(Event e) { System.out.println(e.getSource() + " 在跑步"); }
});
p.eat();  // 輸出: Person@xxx 在吃飯

此模式正是 JavaBean 事件模型與 Servlet 監聽器的理論基礎。

三、Servlet 規範監聽器體系

Servlet 監聽器專門監聽三大域對象的生命週期與屬性變化:

域對象生命週期監聽屬性變化監聽特殊狀態監聽
ServletContextServletContextListenerServletContextAttributeListener—
HttpSessionHttpSessionListenerHttpSessionAttributeListenerHttpSessionBindingListener
HttpSessionActivationListener
ServletRequestServletRequestListenerServletRequestAttributeListener—

四、三大核心生命週期監聽器實戰

1. ServletContextListener:應用級啟動/銷毀


@WebListener  // Servlet 3.0+ 註解註冊,替代 web.xml
public class AppContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        System.out.println("【應用啟動】ServletContext 創建: " + ctx);
        // 典型用途:初始化連接池、載入配置、啟動定時任務
        ctx.setAttribute("appStartTime", System.currentTimeMillis());
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("【應用關閉】ServletContext 銷毀");
        // 典型用途:關閉連接池、停止線程池、釋放資源
    }
}

觸發時機:

  • 創建:容器啟動,為每個 WebApp 創建 ServletContext
  • 銷毀:容器關閉前,銷毀各 WebApp 的 ServletContext

2. HttpSessionListener:會話級創建/銷毀


@WebListener
public class SessionTracker implements HttpSessionListener {
    private static final AtomicInteger activeSessions = new AtomicInteger(0);
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        int count = activeSessions.incrementAndGet();
        System.out.println("【Session創建】ID=" + session.getId() + " 當前活躍=" + count);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        int count = activeSessions.decrementAndGet();
        System.out.println("【Session銷毀】剩餘活躍=" + count);
    }
}

銷毀觸發條件:

  • 超時:web.xml 配置 <session-timeout>30</session-timeout>(分鐘)
  • 手動:調用 session.invalidate()
  • 容器關閉

3. ServletRequestListener:請求級創建/銷毀


@WebListener
public class RequestTracer implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
        long start = System.nanoTime();
        req.setAttribute("requestStartTime", start);
        System.out.println("【請求開始】" + req.getRequestURI() + " from " + req.getRemoteAddr());
    }
    
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
        Long start = (Long) req.getAttribute("requestStartTime");
        long cost = System.nanoTime() - (start != null ? start : 0);
        System.out.println("【請求結束】" + req.getRequestURI() + " 耗時=" + cost/1_000_000 + "ms");
    }
}

觸發頻率:每次 HTTP 請求創建 Request,響應提交後銷毀。

五、屬性監聽器:捕捉域對象層面的增刪改


// 以 ServletContext 為例,其他域同理
@WebListener
public class ContextAttrWatcher implements ServletContextAttributeListener {
    @Override public void attributeAdded(ServletContextAttributeEvent scae) {
        log("新增", scae.getName(), scae.getValue());
    }
    @Override public void attributeRemoved(ServletContextAttributeEvent scae) {
        log("移除", scae.getName(), scae.getValue());
    }
    @Override public void attributeReplaced(ServletContextAttributeEvent scae) {
        log("替換", scae.getName(), "舊值=" + scae.getValue() + " 新值=" + scae.getServletContext().getAttribute(scae.getName()));
    }
    private void log(String op, String name, Object val) {
        System.out.println("【Context屬性" + op + "】" + name + "=" + val);
    }
}

六、特殊狀態監聽器:感知對象綁定/遷移

  • HttpSessionBindingListener:對象實現此介面,當被綁定/解綁至 Session 時自動回調 valueBound/valueUnbound,無需在 web.xml 註冊
  • HttpSessionActivationListener:分布式環境下 Session 鈍化/活化時回調 willPassivate/didActivate

七、註冊方式對比

方式適用版本配置位置優缺點
web.xmlServlet 2.5+<listener><listener-class>...</listener>通用、與代碼分離
@WebListenerServlet 3.0+類上註解簡潔、掃描啟動稍慢
動態註冊Servlet 3.0+ServletContextListener 中 sce.getServletContext().addListener()運行時動態決定

八、最佳實踐與避坑指南

  1. 輕量級處理:監聽器方法同步執行,避免耗時操作阻塞容器啟動/請求處理
  2. 異常隔離:監聽器內部捕獲異常,防止拋出導致容器啟動失敗
  3. 線程安全:屬性監聽器可能多線程並發調用,共享數據需同步
  4. 順序不保證:多個同類監聽器執行順序未規範,勿依賴順序
  5. 區分域作用域:Context(應用級)、Session(用戶級)、Request(請求級),選對域存數據

監聽器是 JavaWeb 應用的「神經系統」,掌握其生命週期觸發點與屬性感知機制,能優雅實現:應用啟動初始化、在線用戶統計、請求鏈路追蹤、分布式 Session 同步等核心功能。建議配合 Filter、AsyncListener 共同構建完整的 Web 基礎設施層。

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