Servlet 過濾器是可插拔的 Web 組件,允許在 Web 應用程式中實作前置與後置處理邏輯。它支援 Servlet 和 JSP 頁面的基礎請求處理功能,如日誌記錄、效能監控、安全控制、會話管理、XSLT 轉換等。過濾器隨 Java Servlet 2.3 規範首次發布,2.4 規範對其進行重大升級。本文將介紹 Servlet 過濾器的基礎架構、實作細節、典型應用,並展望新規範的擴充功能。
Servlet 過濾器是小型 Web 組件,攔截請求與回應,以檢視、擷取或操作客戶端與伺服器間交換的資料。它們封裝重要但非核心的輔助功能,如記錄請求/回應資料、處理安全協定、管理會話屬性等。過濾器提供面向物件的模組化機制,將通用任務封裝為可插拔組件,透過設定檔宣告並動態處理。
web.xml XML 標籤宣告,增刪過濾器無需修改應用程式碼或 JSP。綜上,Servlet 過濾器是透過設定檔靈活宣告的模組化可重用組件,動態處理請求/回應,無需修改程式碼即可透明增刪,且獨立於平台與容器。
過濾器攔截傳入請求及/或傳出回應,監視、修改或處理通過的資料流。它們是自包含的模組化組件,可加入請求/回應鏈或移除而不影響其他 Web 組件。過濾器僅改變請求/回應的運行時處理,不應直接嵌入應用框架,除非透過 Servlet API 標準介面。
Web 資源可配置無過濾器(預設)、單一過濾器(典型)、或過濾器鏈。過濾器接受請求與回應物件,檢查請求決定轉發給鏈中下一組件,或中止請求直接回應客戶端。若轉發,請求傳遞給下一資源(另一過濾器、Servlet 或 JSP)。請求經過鏈被處理後,回應以相反順序通過鏈返回,各過濾器皆有機會處理回應物件。
Servlet 2.3 時過濾器僅能過濾客戶端與指定資源間內容,資源若調度給其他資源,幕後委託請求無法套用過濾器。2.4 規範移除此限制,過濾器可應用於 J2EE Web 環境中任何存在請求/回應物件的地方:客戶端與 Servlet 間、Servlet 與 Servlet/JSP 間、各包含的 JSP 頁面間。這才是真正的強大與靈活!
過濾器 API 包含三個介面:Filter、FilterChain、FilterConfig(均在 javax.servlet 套件)。過濾器類別實作 Filter 介面,使用另外兩個介面。FilterChain 參考傳遞給過濾器,允許將控制權傳給鏈中下一資源;FilterConfig 由容器提供,允許存取初始化資料。
為完整實作 Filter 介面,需實作三個方法:
init(FilterConfig config):容器實例化過濾器時調用,準備處理環境。doFilter(ServletRequest req, ServletResponse res, FilterChain chain):處理請求/回應的核心方法,類似 Servlet 的 service()。destroy():垃圾回收前執行清理操作。以下展示一個簡單過濾器,追蹤滿足客戶端 Web 請求所花費時間:
import javax.servlet.*;
import java.util.*;
import java.io.*;
public class TimeTrackFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
Date startTime, endTime;
double totalTime;
startTime = new Date();
// 轉發請求給鏈中下一資源
chain.doFilter(request, response);
// 處理回應
endTime = new Date();
totalTime = (endTime.getTime() - startTime.getTime()) / 1000; // 毫秒轉秒
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
writer.println();
writer.println("===============");
writer.println("Total elapsed time is: " + totalTime + " seconds.");
writer.println("===============");
writer.flush();
filterConfig.getServletContext().log(sw.getBuffer().toString());
}
}
init(),範例中保存 FilterConfig 參考(雖未使用初始化資料,僅為示範)。doFilter() 由容器調用,傳入請求、回應、鏈參考。過濾器處理請求,調用 chain.doFilter() 轉交下一資源,控制權返回時處理回應。destroy() 執行清理。透過 web.xml 兩個 XML 標籤宣告:<filter> 定義名稱、實作類別、初始化參數;<filter-mapping> 將過濾器關聯至 Servlet 或 URL 模式。
<filter>
<filter-name>Page Request Timer</filter-name>
<filter-class>TimeTrackFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Page Request Timer</filter-name>
<servlet-name>Main Servlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>Main Servlet</servlet-name>
<servlet-class>MainServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Main Servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
上例宣告「Page Request Timer」過濾器映射到「Main Servlet」,並定義 Servlet 映射將所有請求(通配符)發送給該 Servlet。此為控制器組件典型映射。注意宣告順序不可顛倒。
部署極簡單:將過濾器類別與其他 Web 組件類別打包,連同 web.xml(含過濾器定義與映射)置於 Web 應用結構中,Servlet 容器處理其餘一切。
過濾器應用僅受限於創意與設計能力。適用於裝飾器模式或攔截器模式的場合。常見應用包括:
web.xml),生產環境啟用;可疊加多過濾器提升安全等級。模型-視圖-控制器(MVC)架構已整合進 Jakarta Struts、Turbine 等主流 Web 框架。過濾器旨在擴充 MVC 的請求/回應處理流。無論請求/回應發生在客戶端-伺服器間或伺服器組件間,過濾器在處理流中的應用一致。從 MVC 觀點,調度器組件(包含於控制器或與其協作)將請求轉發給適當應用組件處理,使控制器層成為放置 Servlet 過濾器的最佳位置。過濾器置於控制器前可應用於所有請求;置於控制器/調度器與模型/視圖間可針對單一 Web 組件。
雖然過濾器出現僅數年,已成為所有敏捷、面向物件 J2EE Web 應用的關鍵組件。本文介紹了過濾器的高階設計、2.4 與 2.3 規範比較、精確實作步驟、宣告與部署方式、常見應用,以及如何融入傳統 MVC 架構。
這是 J2EE 探索者系列的最後一篇。我們從 Enterprise JavaBean 組件開始,轉向 Web 層,涵蓋 Servlet、JSP、JavaBean 及 Servlet API 無數選擇。與您共同探索是件樂事,感謝參與,祝好運、探索愉快!