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

Servlet Filter 核心機制詳解:過濾器鏈、生命週期、攔截順序與 Dispatcher 類型全覆蓋

     編輯:Java 綜合教學

前言:Filter 是什麼?不是 Servlet!

Filter (過濾器) 是 Web 容器管道中的「預處理/後處理器」:不能直接處理請求生成響應,但能在請求到達 Servlet 前攔截 HttpServletRequest,在 Servlet 響應返回客戶端前攔截 HttpServletResponse。典型場景:編碼統一、登錄驗證、XSS 防護、日誌記錄、壓縮響應。本文深度解析生命週期三方法、過濾器鏈執行流、攔截順序規則、Dispatcher 類型。

一、Filter 生命週期:三大核心方法

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();
}

FilterConfig 作用

  • 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 統一處理。

三、web.xml 配置與攔截順序

<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>

順序鐵律

  1. 聲明順序 = 執行順序:web.xml 中 <filter-mapping> 出現的先後順序決定鏈的順序。
  2. 先聲明的 Filter:doFilter 前置邏輯 先執行,後置邏輯 後執行 (棧帧回彈)。
  3. 多個 <url-pattern> 匹配同一請求時,按映射聲明順序串聯。

四、Dispatcher 類型:攔截請求轉發/包含/錯誤

默認只攔截 客戶端直接請求 (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編碼、登錄驗證、日誌
FORWARDServlet 內部 forward()權限二次檢查、數據補全
INCLUDEJSP <jsp:include> / include()片段頁面統一處理
ERROR容器跳轉錯誤頁面錯誤頁統一佈局、異常記錄
ASYNCstartAsync() 異步處理異步請求超時、清理

五、Spring Boot / Servlet 3.0+ 註冊方式

1. @WebFilter 註解 (Servlet 3.0+)

@WebFilter(
    urlPatterns = "/*",
    dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD},
    initParams = @WebInitParam(name = "encoding", value = "UTF-8")
)
public class EncodingFilter implements Filter { ... }

2. Spring Boot FilterRegistrationBean

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean authFilter() {
        FilterRegistrationBean bean = new FilterRegistrationBean<>();
        bean.setFilter(new AuthFilter());
        bean.addUrlPatterns("/api/*");
        bean.setOrder(1); // 數字越小優先級越高
        return bean;
    }
}

六、最佳實踐與避坑

  1. 輕量化:doFilter 必須極快,避免耗時 IO/計算阻塞整個鏈。
  2. 線程安全:Filter 單例多線程共享,**字段不可存請求狀態**,請用 ThreadLocal 或請求屬性 (request.setAttribute)。
  3. 響應提交後不可操作:chain.doFilter() 返回後,響應可能已提交 (flush/commit),再寫入會拋 IllegalStateException。
  4. 編碼 Filter 必須最先執行:request.setCharacterEncoding 必須在讀取參數前調用。
  5. 異步請求需聲明 ASYNC:否則異步分發不會經過 Filter。

七、總結

Filter = init → doFilter(chain.doFilter) → destroy 生命週期 + 責任鏈模式 + dispatcherTypes 靈活攔截。掌握鏈順序 = web.xml 聲明順序、放行必調 chain.doFilter()、默認只攔 REQUEST、異步需加 ASYNC,即可優雅實現橫切關注點。Spring Boot 時代推薦 FilterRegistrationBean 程序化註冊,@Order 控制優先級,更靈活可測試。

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