監聽器 (Listener) 實現觀察者模式,監聽 Web 容器中的關鍵事件 (上下文啟動/銷毀、Session 創建/銷毀、屬性增刪改),自動觸發業務邏輯 (如統計在線人數、初始化緩存、清理資源)。
| 介面 | 監聽事件 | 核心方法 | 典型應用 |
|---|---|---|---|
| ServletContextListener | Web 應用啟動/關閉 | contextInitialized / contextDestroyed | 初始化數據源、載入配置、啟動定時任務 |
| ServletContextAttributeListener | Application 域屬性增刪改 | attributeAdded / attributeRemoved / attributeReplaced | 審計日誌、集群同步 |
| HttpSessionListener | Session 創建/銷毀 | sessionCreated / sessionDestroyed | 在線人數統計、Session 異常清理 |
| HttpSessionAttributeListener | Session 域屬性增刪改 | attributeAdded / attributeRemoved / attributeReplaced | 購物車變更通知、權限即時刷新 |
| ServletRequestListener | Request 創建/銷毀 | requestInitialized / requestDestroyed | 請求耗時統計、鏈路追蹤 ID 生成 |
| ServletRequestAttributeListener | Request 域屬性增刪改 | attributeAdded / attributeRemoved / attributeReplaced | 參數過濾、安全審計 |
| HttpSessionIdListener | Session ID 變更 | sessionIdChanged | Session Fixation 防護 |
| ServletContextListener (Servlet 4.0+) | 同上 | 同上 | 同上 |
@WebListener
public class OnlineCounter implements HttpSessionListener {
private static final AtomicInteger ONLINE_COUNT = new AtomicInteger(0);
@Override
public void sessionCreated(HttpSessionEvent se) {
int cnt = ONLINE_COUNT.incrementAndGet();
se.getSession().getServletContext().setAttribute("onlineCount", cnt);
System.out.println("用戶上線,當前在線:" + cnt);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
int cnt = ONLINE_COUNT.decrementAndGet();
se.getSession().getServletContext().setAttribute("onlineCount", cnt);
System.out.println("用戶下線,當前在線:" + cnt);
}
public static int getOnlineCount() { return ONLINE_COUNT.get(); }
}
@WebListener 註解,Servlet 3.0+ 自動掃描)<listener>
<listener-class>com.example.OnlineCounter</listener-class>
</listener>
<%@ page contentType="text/html;charset=UTF-8" %>
<html><body>
當前在線用戶數:${applicationScope.onlineCount}
</body></html>
@WebListener
public class AppInitializer implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
// 1. 載入配置
String configPath = ctx.getInitParameter("configPath");
// 2. 初始化連接池/緩存
DataSource ds = createDataSource();
ctx.setAttribute("dataSource", ds);
// 3. 啟動後台任務
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> refreshCache(ds), 0, 1, TimeUnit.HOURS);
ctx.setAttribute("scheduler", scheduler);
System.out.println("應用啟動完成");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
// 關閉資源
((ScheduledExecutorService) ctx.getAttribute("scheduler")).shutdown();
((DataSource) ctx.getAttribute("dataSource")).close();
System.out.println("應用關閉,資源已釋放");
}
}
AtomicInteger/ConcurrentHashMap。contextInitialized 拋異常會導致應用啟動失敗,關鍵初始化務必 try-catch 並記錄錯誤。contextDestroyed 必須關閉線程池、連接池、定時任務,否則內存洩漏。Servlet 監聽器是 Web 應用「感知容器生命週期」的官方擴展點。熟練掌握八大介面、註冊方式、線程安全與資源管理,能優雅實現啟動初始化、在線統計、審計日誌、緩存刷新等橫切功能,保持 Controller/Service 純淨專注業務。