在 Java Web 開發中,Servlet 與 Filter 是兩個核心組件,雖然它們都處理 HTTP 請求與回應,但職責與運作模式截然不同。本文將深入剖析兩者的生命週期、核心差異、鏈式調用機制,並附上實戰配置範例,幫助開發者釐清觀念、靈活運用。
| 特性 | Servlet | Filter |
|---|---|---|
| 主要職責 | 處理業務邏輯、產生動態回應 | 預處理請求、後處理回應、通用邏輯複用 |
| 能否產生回應 | 是,可直接輸出內容 | 否,只能修改請求/回應物件 |
| 生命週期方法 | init、service、destroy | init、doFilter、destroy |
| 調用方式 | 容器根據 URL 映射直接調用 | 鏈式調用,經由 FilterChain 傳遞 |
| 典型應用場景 | Controller、REST API、頁面渲染 | 編碼過濾、權限驗證、日誌記錄、XSS 防護 |
init(FilterConfig config)Web 容器啟動時載入 Filter 實例,僅呼叫一次。可透過 FilterConfig 取得 <init-param> 設定的初始化參數(如字元編碼、白名單路徑等)。
doFilter(request, response, chain)每個請求到達時皆會觸發。核心邏輯:
ServletRequest/ServletResponse、記錄日誌、驗證權限等。chain.doFilter(request, response):將控制權傳遞給鏈中下一個 Filter 或目標 Servlet。chain.doFilter 返回後,可修改回應標頭、壓縮輸出、計算耗時等。destroy()容器關閉或 Web 應用卸載前呼叫,用於釋放資源(關閉資料庫連線池、停止排程任務等)。
請求流向:Client → Filter1 → Filter2 → Filter3 → … → Target Servlet 回應流向:Target Servlet → … → Filter3 → Filter2 → Filter1 → Client
chain.doFilter() 如同一扇「雙向門」:請求通過時向下傳遞,回應返回時向上回溯。若某 Filter 未呼叫 chain.doFilter(),鏈條即中斷,後續 Filter 與 Servlet 皆不會被執行——這常用於權限攔截、快取直接回應等場景。
<!-- 編碼過濾器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.example.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 登入驗證過濾器 -->
<filter>
<filter-name>authFilter</filter-name>
<filter-class>com.example.filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/secure/*</url-pattern>
</filter-mapping>
<!-- 目標 Servlet -->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.example.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/secure/user</url-pattern>
</servlet-mapping>
關鍵細節: <filter-mapping> 的順序決定鏈條順序;url-pattern 需精準匹配攔截路徑,避免過度攔截靜態資源。
現代專案建議使用 @WebFilter 與 @WebServlet 替代 XML:
@WebFilter(urlPatterns = "/*", initParams = @WebInitParam(name="encoding", value="UTF-8"))
public class EncodingFilter implements Filter { ... }
@WebServlet("/secure/user")
public class UserServlet extends HttpServlet { ... }
chain.doFilter() 導致請求卡死 → 正確:除非刻意攔截(如未登入導向登入頁),否則務必呼叫。HttpServletRequestWrapper)。Servlet 是「主角」,負責核心業務;Filter 是「幕後推手」,負責通用預處理與後處理。理解兩者的生命週期、鏈式協作與配置細節,能讓你設計出結構清晰、易維護且高效能的 Java Web 應用。