實作 Filter 介面是定義 Filter 服務的方式,然而,在 Servlet 4.0 中,新增了 GenericFilter 類別,目的類似於 GenericServlet,GenericFilter 將 FilterConfig 的設定、Filter 初始參數的取得做了封裝,來看看它的原始碼:
package javax.servlet;
import java.io.Serializable;
import java.util.Enumeration;
public abstract class GenericFilter implements Filter, FilterConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private volatile FilterConfig filterConfig;
    @Override
    public String getInitParameter(String name) {
        return getFilterConfig().getInitParameter(name);
    }
    @Override
    public Enumeration<String> getInitParameterNames() {
        return getFilterConfig().getInitParameterNames();
    }
    public FilterConfig getFilterConfig() {
        return filterConfig;
    }
    @Override
    public ServletContext getServletContext() {
        return getFilterConfig().getServletContext();
    }
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig  = filterConfig;
        init();
    }
    public void init() throws ServletException {
    }
    
    @Override
    public String getFilterName() {
        return getFilterConfig().getFilterName();
    }
}
因此若是 GenericFilter 的子類別,要定義 Filter 的初始化,可以重新定義無參數 init() 方法了,Servlet 4.0 之中,也新增了 HttpFilter,繼承自 GenericFilter,對於 HTTP 方法的處理,新增了另一個版本的 doFilter() 方法:
package javax.servlet.http;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.GenericFilter;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public abstract class HttpFilter extends GenericFilter {
    private static final long serialVersionUID = 1L;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)) {
            throw new ServletException(request + " not HttpServletRequest");
        }
        if (!(response instanceof HttpServletResponse)) {
            throw new ServletException(request + " not HttpServletResponse");
        }
        doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
    }
    protected void doFilter(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
}
因此,在 Servlet 4.0 中,若要定義 Filter,可以繼承 HttpFilter,並重新定義 HttpServletRequest、HttpServletResponse 版本的 doFilter() 方法。
例如,以下實作一個簡單的效能量測過濾器,可用來記錄請求與回應間的時間差,了解Servlet處理請求到回應所需花費的時間。
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebFilter("/*")
public class PerformanceFilter extends HttpFilter {
    @Override
    protected void doFilter(
         HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                throws IOException, ServletException {
         long begin = System.currentTimeMillis();
            chain.doFilter(request, response);
            getServletContext().log("Request process in " +
                    (System.currentTimeMillis() - begin) + " milliseconds");
    }
}
當過濾器類別被載入容器時並實例化後,容器會執行其 init() 方法並傳入 FilterConfig 物件作為參數,呼叫無參數 init() 方法。
當請求來到 Filter,會呼叫 Filter 介面的 doFilter(),若是 HttpFilter 的子類別,就會進一步呼叫有 HttpServletRequest 參數的 doFilter() 方法。
在 doFilter() 的實作中,先記錄目前的系統時間,接著呼叫 FilterChain 的 doFilter() 繼續接下來的過濾器或 Servlet,當 FilterChain 的 doFilter() 返回時,取得系統時間並減去先前記錄的時間,就是請求與回應間的時間差。
過濾器的設定與 Servlet 的設定很類似。@WebFilter 中的 filterName 設定過濾器名稱,預設值是類別完全吻合名稱,urlPatterns 設定哪些 URL 請求必須套用哪個過濾器,可套用的 URL 模式與 Servlet 基本上相同,而 /* 表示套用在所有的 URL 請求。
如果要在 web.xml 中設定,則可以如下:
<web-app ...>
    <filter>
        <filter-name>performance</filter-name>
        <filter-class>cc.openhome.PerformanceFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>performance</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    // 略...
</web-app>
<filter> 標籤中使用 <filter-name> 與 <filter-class> 設定過濾器名稱與類別名稱。而在 <filter-mapping> 中,則用 <filter-name> 與 <url-pattern> 來設定哪些 URL 請求必須套用哪個過濾器。
在過濾器的請求套用上,除了指定 URL 模式之外,你也可以指定 Servlet 名稱,這可以透過 @WebFilter 的 servletNames 來設定:
@WebFilter(servletNames={"SomeServlet"})
或 web.xml 中,在 <filter-mapping> 中使用 <servlet-name> 來設定:
<filter-mapping>
    <filter-name>performance</filter-name>
    <servlet-name>SomeServlet</servlet-name>
</filter-mapping>
如果想一次符合所有的 Servlet 名稱,則可以使用星號(*)。如果在過濾器初始化時,想要讀取一些參數,可以在 @WebFilter 中使用 @WebInitParam 設定 initParams。例如:
...
@WebFilter(
    urlPatterns={"/*"}, 
    initParams={
        @WebInitParam(name = "PARAM1", value = "VALUE1"),
        @WebInitParam(name = "PARAM2", value = "VALUE2")
    }
)
public class PerformanceFilter extends HttpFilter {
    private String PARAM1;
    private String PARAM2;
    @Override
    public void init() throws ServletException {
        PARAM1 = getInitParameter("PARAM1");
        PARAM2 = getInitParameter("PARAM2");
    }
   ...
}
若要在 web.xml 中設定過濾器的初始參數,可以在 <filter> 標籤之中,使用 <init-param> 進行設定, web.xml 中的設定會覆蓋標註的設定。例如:
...
    <filter>
        <filter-name>PerformanceFilter</filter-name>
        <filter-class>cc.openhome.PerformanceFilter</filter-class>
        <init-param>
            <param-name>PARAM1</param-name>
            <param-value>VALUE1</param-value>
        </init-param>
        <init-param>
            <param-name>PARAM2</param-name>
            <param-value>VALUE2</param-value>
        </init-param>
    </filter>
...
觸發過濾器的時機,預設是瀏覽器直接發出請求。如果是那些透過 RequestDispatcher 的 forward() 或 include() 的請求,設定 @WebFilter 的 dispatcherTypes。例如:
@WebFilter(
    urlPatterns={"/some"}, 
    dispatcherTypes={
        DispatcherType.FORWARD, DispatcherType.INCLUDE, 
        DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC
    }
)
如果不設定任何 dispatcherTypes,則預設為 REQUEST。FORWARD 就是指透過 RequestDispatcher 的 forward() 而來的請求 可以套用過濾器。INCLUDE 就是指透過 RequestDispatcher 的 include() 而來的請求可以套用過濾器。ERROR 是指由容器處理 例外而轉發過來的請求可以觸發過濾器。ASYNC 是指非同步處理的請求可以觸發過濾器(之後還會說明非同步處理)。
若要在 web.xml 中設定,則可以使用 <dispatcher>標籤。例如:
...
<filter-mapping>
    <filter-name>SomeFilter</filter-name>
    <servlet-name>*.do</servlet-name>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>
...
你可以透過 <url-pattern> 或 <servlet-name> 來指定,哪些 URL 請求或哪些 Servlet 可套用過濾器。如果同時具備 <url-pattern> 與 <servlet-name>,則先比對 <url-pattern>,再比對 <servlet-name>。如果有某個 URL 或 Servlet 會套用多個過濾器,則根據 <filter-mapping> 在 web.xml 中出現的先後順序,來決定過濾器的執行順序。

