Filter (過濾器) 是 Web 容器管道中的「預處理/後處理器」:不能直接處理請求生成響應,但能在請求到達 Servlet 前攔截 HttpServletRequest,在 Servlet 響應返回客戶端前攔截 HttpServletResponse。典型場景:編碼統一、登錄驗證、XSS 防護、日誌記錄、壓縮響應。本文深度解析生命週期三方法、過濾器鏈執行流、攔截順序規則、Dispatcher 類型。
public interface Filter {
// 1. 初始化:容器啟動時調用,僅一次,類似 Servlet.init()
void init(FilterConfig filterConfig) throws ServletException;
// 2. 核心過濾邏輯:每個請求調用一次
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
// 3. 銷毀:容器關閉/卸載時調用,僅一次,釋放資源
void destroy();
}
getInitParameter(name):讀取 web.xml 配置的初始化參數。getServletContext():獲取應用上下文,訪問全局資源。// 典型 doFilter 模板
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
// 1. 前置處理 (預處理 Request)
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 例:登錄驗證
if (needLogin(request) && !isLoggedIn(request)) {
response.sendRedirect("/login"); // 直接響應,鏈終止!
return; // 關鍵:不調用 chain.doFilter()
}
// 2. 傳遞給下一個過濾器或目標資源
chain.doFilter(request, response); // ★ 核心:放行
// 3. 後置處理 (後處理 Response)
// 例:統一添加響應頭、壓縮、日誌記錄
response.setHeader("X-Frame-Options", "DENY");
}
chain.doFilter() 必須調用 才能繼續鏈;不調用 = 直接攔截結束 (如驗證失敗跳轉)。doFilter 前,後置邏輯在 doFilter 後 (類似 AOP 環繞)。doFilter 中 try-catch 統一處理。<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.example.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-mapping>
<filter-name>authFilter</filter-name>
<url-pattern>/api/*</url-pattern> <!-- 僅攔截 /api/* -->
</filter-mapping>
web.xml 中 <filter-mapping> 出現的先後順序決定鏈的順序。doFilter 前置邏輯 先執行,後置邏輯 後執行 (棧帧回彈)。<url-pattern> 匹配同一請求時,按映射聲明順序串聯。默認只攔截 客戶端直接請求 (REQUEST)。若需攔截 forward、include、錯誤頁面,需在 <filter-mapping> 顯式聲明:
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher> <!-- 默認:直接請求 -->
<dispatcher>FORWARD</dispatcher> <!-- RequestDispatcher.forward() -->
<dispatcher>INCLUDE</dispatcher> <!-- RequestDispatcher.include() -->
<dispatcher>ERROR</dispatcher> <!-- <error-page> 錯誤頁面 -->
<dispatcher>ASYNC</dispatcher> <!-- Servlet 3.0+ 異步請求 -->
</filter-mapping>
| Dispatcher 類型 | 觸發場景 | 典型用途 |
|---|---|---|
| REQUEST | 瀏覽器直接訪問 URL | 編碼、登錄驗證、日誌 |
| FORWARD | Servlet 內部 forward() | 權限二次檢查、數據補全 |
| INCLUDE | JSP <jsp:include> / include() | 片段頁面統一處理 |
| ERROR | 容器跳轉錯誤頁面 | 錯誤頁統一佈局、異常記錄 |
| ASYNC | startAsync() 異步處理 | 異步請求超時、清理 |
@WebFilter(
urlPatterns = "/*",
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD},
initParams = @WebInitParam(name = "encoding", value = "UTF-8")
)
public class EncodingFilter implements Filter { ... }
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean authFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean<>();
bean.setFilter(new AuthFilter());
bean.addUrlPatterns("/api/*");
bean.setOrder(1); // 數字越小優先級越高
return bean;
}
}
doFilter 必須極快,避免耗時 IO/計算阻塞整個鏈。ThreadLocal 或請求屬性 (request.setAttribute)。chain.doFilter() 返回後,響應可能已提交 (flush/commit),再寫入會拋 IllegalStateException。request.setCharacterEncoding 必須在讀取參數前調用。Filter = init → doFilter(chain.doFilter) → destroy 生命週期 + 責任鏈模式 + dispatcherTypes 靈活攔截。掌握鏈順序 = web.xml 聲明順序、放行必調 chain.doFilter()、默認只攔 REQUEST、異步需加 ASYNC,即可優雅實現橫切關注點。Spring Boot 時代推薦 FilterRegistrationBean 程序化註冊,@Order 控制優先級,更靈活可測試。