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

Tomcat 4 Servlet 2.3 Filter 完整指南:從原理到實戰的兩個過濾器範例

     編輯:Java 相關知識

Filter 在 J2EE 請求處理管道中的定位

Servlet 2.3 規範引入 Filter 機制,Tomcat 4 率先實現。不同於傳統 Web 伺服器的「掛鉤函數」模式,Servlet Filter 採用嵌套調用鏈架構:

  • 請求到達 → Filter 鏈依序執行 doFilter(request, response, chain)
  • 每個 Filter 可決定:放行(chain.doFilter())、攔截並自行響應、修改請求/響應物件
  • 響應返回時逆序經過 Filter 鏈,可進行後處理(壓縮、加密、記錄等)

Filter 與 J2EE 請求處理流程

Filter 介面三大生命週期方法

public interface Filter {
    void init(FilterConfig config);                    // 容器啟動時調用一次
    void doFilter(ServletRequest req, ServletResponse res, FilterChain chain);  // 每請求調用
    void destroy();                                    // 容器關閉時調用
}

部署描述符配置:web.xml 核心標籤

<web-app>
    <!-- 1. 過濾器定義 -->
    <filter>
        <filter-name>IE Filter</filter-name>
        <filter-class>com.ibm.devworks.filters.IEFilter</filter-class>
        <init-param>  <!-- 可選初始化參數 -->
            <param-name>search</param-name>
            <param-value>cannot</param-value>
        </init-param>
    </filter>

    <!-- 2. 過濾器映射:決定攔截哪些 URL -->
    <filter-mapping>
        <filter-name>IE Filter</filter-name>
        <url-pattern>/*</url-pattern>  <!-- 攔截所有請求 -->
    </filter-mapping>
</web-app>

關鍵規則:<filter-mapping> 在 web.xml 中的出現順序決定鏈執行順序,這直接影響業務邏輯結果。

實戰範例一:IEFilter - 基於 User-Agent 的存取控制

public final class IEFilter implements Filter {
    private FilterConfig filterConfig;

    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        String ua = ((HttpServletRequest) request).getHeader("User-Agent").toLowerCase();
        
        if (ua.contains("msie")) {  // 檢測到 IE
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println("

Sorry, page cannot be displayed!

"); out.flush(); return; // 關鍵:不調用 chain.doFilter(),直接終止後續處理 } chain.doFilter(request, response); // 非 IE 正常放行 } public void init(FilterConfig config) { this.filterConfig = config; } public void destroy() {} }

測試結果:IE 訪問顯示自定義拒絕頁;Netscape/Firefox 正常顯示原頁面。

IE 訪問被攔截畫面 Netscape 正常訪問畫面

實戰範例二:ReplaceTextFilter - 響應內容動態替換(包裝模式)

核心難點:如何攔截並修改 Servlet/JSP 的輸出流? 答案是包裝響應物件。

1. 自定義輸出流:ReplaceTextStream

class ReplaceTextStream extends ServletOutputStream {
    private OutputStream realStream;      // 真實響應流
    private ByteArrayOutputStream buffer; // 緩衝所有寫入
    private String searchText, replaceText;

    public ReplaceTextStream(OutputStream out, String search, String replace) {
        this.realStream = out;
        this.buffer = new ByteArrayOutputStream();
        this.searchText = search;
        this.replaceText = replace;
    }

    public void write(int b) { buffer.write(b); }  // 所有寫入導向緩衝區

    // 關閉/刷新時執行實際替換並寫入真實流
    public void close() throws IOException {
        if (!closed) { processStream(); realStream.close(); closed = true; }
    }
    public void flush() throws IOException {
        if (buffer.size() > 0 && !closed) { processStream(); buffer.reset(); }
    }

    private void processStream() throws IOException {
        byte[] transformed = replaceContent(buffer.toByteArray());
        realStream.write(transformed);
        realStream.flush();
    }

    private byte[] replaceContent(byte[] input) {
        String content = new String(input);
        int idx = content.toLowerCase().indexOf(searchText.toLowerCase());
        if (idx == -1) return input;
        String result = content.substring(0, idx) + replaceText + 
                        content.substring(idx + searchText.length());
        return result.getBytes();
    }
}

2. 包裝響應物件:ReplaceTextWrapper

class ReplaceTextWrapper extends HttpServletResponseWrapper {
    private ReplaceTextStream customStream;
    private PrintWriter customWriter;

    public ReplaceTextWrapper(ServletResponse response, String search, String replace) 
            throws IOException {
        super((HttpServletResponse) response);
        customStream = new ReplaceTextStream(response.getOutputStream(), search, replace);
        customWriter = new PrintWriter(customStream);
    }

    @Override public ServletOutputStream getOutputStream() { return customStream; }
    @Override public PrintWriter getWriter() { return customWriter; }
}

3. Filter 主邏輯

public final class ReplaceTextFilter implements Filter {
    private String searchText = ".", replaceText = ".";

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        ReplaceTextWrapper wrapped = new ReplaceTextWrapper(res, searchText, replaceText);
        chain.doFilter(req, wrapped);  // 後續組件寫入的是包裝流
        wrapped.getOutputStream().close();  // 強制觸發替換邏輯
    }

    public void init(FilterConfig config) {
        String s = config.getInitParameter("search");
        if (s != null) searchText = s;
        s = config.getInitParameter("replace");
        if (s != null) replaceText = s;
    }
    public void destroy() {}
}

web.xml 配置範例

<filter>
    <filter-name>Replace Text Filter</filter-name>
    <filter-class>com.ibm.devworks.filters.ReplaceTextFilter</filter-class>
    <init-param><param-name>search</param-name><param-value>cannot</param-value></init-param>
    <init-param><param-name>replace</param-name><param-value>must not</param-value></init-param>
</filter>
<filter-mapping>
    <filter-name>Replace Text Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

效果:所有頁面輸出中的 "cannot" 自動變為 "must not",包含 JSP 動態生成內容。

Filter 鏈順序的決定性影響

同一 url-pattern 下,<filter-mapping> 先後順序決定執行順序:

  • IEFilter 在前:IE 請求被直接攔截,ReplaceTextFilter 根本不執行 → 輸出 "cannot be displayed"
  • ReplaceTextFilter 在前:先包裝響應流,IEFilter 寫入自定義頁面時也會被替換 → 輸出 "must not be displayed"

實戰原則:web.xml 中映射順序即業務邏輯順序,設計時必須明確規劃。

架構優勢總結

  1. 可移植性:標準 API,支援所有 Servlet 2.3+ 容器。
  2. 關注點分離:認證、日誌、壓縮、編碼轉換等橫切關注點剝離出業務 Servlet。
  3. 動態組合:透過部署描述符組裝處理管線,無需修改代碼。
  4. 高效能基礎:嵌套調用保持棧上下文,局部變量天然共享,避免 ThreadLocal 濫用。

掌握 Filter 包裝模式(HttpServletResponseWrapper + 自定義 Stream),即掌握了「響應後處理」的通用鑰匙——無論是 GZIP 壓縮、XSLT 轉換、還是敏感詞過濾,本質皆是如此。

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