一、Filter的介紹及使用
什麼是過濾器?
與Servlet相似,過濾器是一些web應用程序組件,可以綁定到一個web應用程序中。但是與其他web應用程序組件不同的是,過濾器是"鏈"在容器的處理過程中的。這就意味著它們會在servlet處理器之前訪問一個進入的請求,並且在外發響應信息返回到客戶前訪問這些響應信息。這種訪問使得過濾器可以檢查並修改請求和響應的內容。
過濾器適用於那些地方?
l 為一個web應用程序的新功能建立模型(可被添加到web應用程序中或者從web應用程序中刪除而不需要重寫基層應用程序代碼);
l 向過去的代碼添加新功能。
過濾器放在容器結構的什麼位置?
過濾器放在web資源之前,可以在請求抵達它所應用的web資源(可以是一個Servlet、一個Jsp頁面,甚至是一個HTML頁面)之前截獲進入的請求,並且在它返回到客戶之前截獲輸出請求。Filter:用來攔截請求,處於客戶端與被請求資源之間,目的是重用代碼。Filter鏈,在web.xml中哪個先配置,哪個就先調用。在filter中也可以配置一些初始化參數。
Java中的Filter 並不是一個標准的Servlet ,它不能處理用戶請求,也不能對客戶端生成響應。 主要用於對HttpServletRequest 進行預處理,也可以對HttpServletResponse 進行後處理,是個典型的處理鏈。
Filter 有如下幾個用處:
l 在HttpServletRequest 到達Servlet 之前,攔截客戶的HttpServletRequest 。
l 根據需要檢查HttpServletRequest ,也可以修改HttpServletRequest 頭和數據。
l 在HttpServletResponse 到達客戶端之前,攔截HttpServletResponse 。
l 根據需要檢查HttpServletResponse ,可以修改HttpServletResponse 頭和數據。
Filter 有如下幾個種類:
l 用戶授權的Filter: Filter 負責檢查用戶請求,根據請求過濾用戶非法請求。
l 日志Filter: 詳細記錄某些特殊的用戶請求。
l 負責解碼的Filter: 包括對非標准編碼的請求解碼。
l 能改變XML 內容的XSLTFilter 等。
一個Filter 可負責攔截多個請求或響應:一個請求或響應也可被多個請求攔截。
創建一個Filter 只需兩個步驟:
(1)創建Filter 處理類:
(2)在web.xml 文件中配置Filter 。
創建Filter 必須實現javax.servlet.Filter 接口,在該接口中定義了三個方法。
• void init(FilterConfig config): 用於完成Filter 的初始化。
• void destroy(): 用於Filter 銷毀前,完成某些資源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 實現過濾功能,該方法就是對每個請求及響應增加的額外處理。
過濾器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驅動。在servlet2.4中,過濾器同樣可以用於請求分派器,但須在web.xml中聲明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>該元素位於filter-mapping中。
Filter常用的場景:
一個實例
首先來看一下web.xml的配置:
<!-- 請求url日志記錄過濾器 --> <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>
然後看一下編碼過濾器:
package com.weijia.filterservlet; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class EncodingFilter implements Filter { private String encoding; private HashMap<String,String> params = new HashMap<String,String>(); // 項目結束時就已經進行銷毀 public void destroy() { System.out.println("end do the encoding filter!"); params=null; encoding=null; } public void doFilter(ServletRequest req, ServletResponse resp,FilterChain chain) throws IOException, ServletException { System.out.println("before encoding " + encoding + " filter!"); req.setCharacterEncoding(encoding); chain.doFilter(req, resp); System.out.println("after encoding " + encoding + " filter!"); System.err.println("----------------------------------------"); } // 項目啟動時就已經進行讀取 public void init(FilterConfig config) throws ServletException { System.out.println("begin do the encoding filter!"); encoding = config.getInitParameter("encoding"); for (Enumeration<?> e = config.getInitParameterNames(); e.hasMoreElements();) { String name = (String) e.nextElement(); String value = config.getInitParameter(name); params.put(name, value); } } }
日志過濾器:
package com.weijia.filterservlet; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class LogFilter implements Filter { public FilterConfig config; public void destroy() { this.config = null; System.out.println("end do the logging filter!"); } public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { System.out.println("before the log filter!"); // 將請求轉換成HttpServletRequest 請求 HttpServletRequest hreq = (HttpServletRequest) req; // 記錄日志 System.out.println("Log Filter已經截獲到用戶的請求的地址:"+hreq.getServletPath() ); try { // Filter 只是鏈式處理,請求依然轉發到目的地址。 chain.doFilter(req, res); } catch (Exception e) { e.printStackTrace(); } System.out.println("after the log filter!"); } public void init(FilterConfig config) throws ServletException { System.out.println("begin do the log filter!"); this.config = config; } }
測試Servlet:
package com.weijia.filterservlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FilterServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setDateHeader("expires", -1); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
訪問FilterServlet
運行結果:
before the log filter!
Log Filter已經截獲到用戶的請求的地址:/FilterServlet
before encoding utf-8 filter!
after encoding utf-8 filter!
----------------------------------------
after the log filter!
我們從運行結果可以看到這個過濾器的調用關系:
類似於C++中的構造函數和析構函數的調用順序,
這裡我們在web.xml中注冊的是先注冊日志過濾器的,然後再注冊
當我們重新部署應用的時候發現:
會先銷毀上次的過濾器,然後再重新注冊一下