過濾器是 Web 應用程式組件,可綁定至 Web 應用。不同於 Servlet,過濾器以「鏈」式串接在容器處理流程中:
位於客戶端與目標資源(Servlet/JSP/HTML)之間,形成 Filter Chain。web.xml 中配置順序決定調用順序。也可配置初始化參數。
init() → doFilter() → destroy()
由部署描述符中的 <filter> 元素驅動。Servlet 2.4 起支援請求分派器(<dispatcher>INCLUDE|FORWARD|REQUEST|ERROR</dispatcher>)。
public interface Filter {
void init(FilterConfig config) throws ServletException;
void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException;
void destroy();
}
<!-- 日誌過濾器 -->
<filter>
<filter-name>logfilter</filter-name>
<filter-class>com.weijia.filterservlet.LogFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 編碼過濾器 -->
<filter>
<filter-name>setCharacterEncoding</filter-name>
<filter-class>com.weijia.filterservlet.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class EncodingFilter implements Filter {
private String encoding;
private Map<String, String> params = new HashMap<>();
@Override public void init(FilterConfig config) {
System.out.println("begin do the encoding filter!");
encoding = config.getInitParameter("encoding");
for (Enumeration<?> e = config.getInitParameterNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
params.put(name, config.getInitParameter(name));
}
}
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("before encoding " + encoding + " filter!");
req.setCharacterEncoding(encoding);
chain.doFilter(req, res); // 關鍵:傳遞給下一個過濾器或目標資源
System.out.println("after encoding " + encoding + " filter!");
}
@Override public void destroy() {
System.out.println("end do the encoding filter!");
params = null; encoding = null;
}
}
public class LogFilter implements Filter {
@Override public void init(FilterConfig config) {
System.out.println("begin do the log filter!");
}
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
System.out.println("before the log filter!");
HttpServletRequest hreq = (HttpServletRequest) req;
System.out.println("Log Filter攔截到請求路徑: " + hreq.getServletPath());
try {
chain.doFilter(req, res);
} catch (Exception e) { e.printStackTrace(); }
System.out.println("after the log filter!");
}
@Override public void destroy() {
System.out.println("end do the log filter!");
}
}
@WebServlet("/FilterServlet")
public class FilterServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setDateHeader("expires", -1);
}
}
before the log filter!
Log Filter攔截到請求路徑:/FilterServlet
before encoding utf-8 filter!
after encoding utf-8 filter!
----------------------------------------
after the log filter!
類似 C++ 建構/解構函數順序:web.xml 先配置的 Filter,init() 先執行,doFilter() 先進後出,destroy() 後執行。
容器先銷毀舊 Filter(逆序 destroy()),再初始化新 Filter(順序 init())。