1、Filter(過濾器)的基本介紹
Servlet 過濾器可以動態地攔截請求和響應,以變換或使用包含在請求或響應中的信息。可以將一個或多個 Servlet 過濾器附加到一個 Servlet 或一組 Servlet。Servlet 過濾器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 頁面。
調用 Servlet 前調用所有附加的 Servlet 過濾器。
Servlet 過濾器是可用於 Servlet 編程的 Java 類,可以實現以下目的:
- 在客戶端的請求訪問后端資源之前,攔截這些請求。
- 在服務器的響應發送回客戶端之前,處理這些響應。
Filter、Listener和 servlet 是 Java EE 的三大組件。
1.1、過濾器的作用
過濾器一般用於完成一些通用的操作,將一些公用邏輯從各個Servlet中抽離出來在過濾器里實現。比如:
- 記錄日志、登錄檢查、全局設置等;
- 統一編碼設置、敏感字符過濾等等
在HTTP請求到達Servlet之前,我們可以通過一個或多個Filter 對 servlet 進行預處理。
2、Filter的基本使用
過濾器是一個實現了 javax.servlet.Filter 接口的 Java 類。
javax.servlet.Filter 接口定義了三個方法:
- public void doFilter (ServletRequest, ServletResponse, FilterChain):該方法完成實際的過濾操作,當客戶端請求方法與過濾器設置匹配的URL時,Servlet容器將先調用過濾器的doFilter方法。每一次請求被攔截時都會執行。
- public void init(FilterConfig filterConfig):服務器啟動后將會自動創建 Filter 的實例對象,並自動調用其 init 方法,讀取web.xml配置,完成對象的初始化功能,從而為后續的用戶請求作好攔截的准備工作(filter對象只會創建一次,init方法也只會執行一次)
- public void destroy():正常關閉服務器時,Filter 對象被銷毀,將會執行該方法(正常關閉時才會執行該方法)。一般可在該方法中釋放過濾器占用的資源。
定義一個 filter 跟定義一個 java 類沒什么差別。示例如下:
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/*") //定義攔截的URL public class FilterTest01 implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("filter被執行了。。"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); //將請求放行 } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
上面配置的過濾器過濾的URL為 /*,則表示所有路徑的資源都會被過濾器攔截到並進行處理。不僅僅是 servlet,只要是訪問服務器上的資源,比如 jsp、html、css、js 、圖片等等,這些也都能被過濾器攔截到。
在 doFilter() 方法內部,要將該請求放行,允許后面的資源或者后面的 Filter 處理請求,則必須調用 chain.doFilter(req, resp)。否則,請求雖然能成功,但不會有內容返回,默認響應是200+空白輸出,比如如果請求的是 jsp 或者是 html 資源,將會看到空白頁面。
在 chain.doFilter(req, resp) 前面的語句會在資源響應前先執行這些代碼,但在 chain.doFilter(req, resp) 后面的語句將會在資源返回后執行,所以一般這些語句可用於對響應的信息進行操作。
注意,doFilter() 中的參數是 ServletRequest,而不是 servlet 中的 HttpServletRequest。HttpServletRequest和ServletRequest都是接口,HttpServletRequest繼承自ServletRequest。HttpServletRequest比ServletRequest多了一些針對於Http協議的方法。 例如:getHeader(), getMethod() , getSession() 等。
要想使用 HttpServletRequest 中的參數,應該對 ServletRequest 進行強轉換:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest)request; String uri = httpServletRequest.getRequestURI(); }
2.1、使用 web.xml 配置過濾器
除了可以使用注解 @WebFilter() 來配置過濾器,我們也可以使用 web.xml 來配置過濾器。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>filterTestName</filter-name> <!-- filter名稱 --> <filter-class>test.FilterTest01</filter-class> <!-- 完整類名 --> </filter> <filter-mapping> <filter-name>filterTestName</filter-name> <url-pattern>/*</url-pattern> <!-- 攔截路徑 --> </filter-mapping> </web-app>
跟 servlet 的配置類型,其中,filter-class 是完整的類名,filter-name 給你所配置的過濾器指定了一個名稱,下面的 filter-mapping 里面的 filter-name 跟上面的 filter-name 相匹配,url-pattern 指定的是該過濾器的攔截路徑。
使用 web.xml 配置過后注解就可以不使用了。
web.xml配置各節點說明:
<filter>
指定一個過濾器。<filter-name>
用於為過濾器指定一個名字,該元素的內容不能為空。<filter-class>
元素用於指定過濾器的完整的限定類名。<init-param>
元素用於為過濾器指定初始化參數,它的子元素<param-name>
指定參數的名字,<param-value>
指定參數的值。- 在過濾器中,可以使用
FilterConfig
接口對象來訪問初始化參數。
<filter-mapping>
元素用於設置一個 Filter 所負責攔截的資源。一個Filter攔截的資源可通過兩種方式來指定:Servlet 名稱和資源訪問的請求路徑<filter-name>
子元素用於設置filter的注冊名稱。該值必須是在<filter>
元素中聲明過的過濾器的名字<url-pattern>
設置 filter 所攔截的請求路徑(過濾器關聯的URL樣式)
<servlet-name>
指定過濾器所攔截的Servlet名稱。<dispatcher>
指定過濾器所攔截的資源被 Servlet 容器調用的方式,可以是REQUEST
,INCLUDE
,FORWARD
和ERROR
之一,默認REQUEST
。用戶可以設置多個<dispatcher>
子元素用來指定 Filter 對資源的多種調用方式進行攔截。<dispatcher>
子元素可以設置的值及其意義:REQUEST
(默認值):當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那么該過濾器就不會被調用。INCLUDE
:如果目標資源是通過RequestDispatcher的include()方法訪問時,那么該過濾器將被調用。除此之外,該過濾器不會被調用。FORWARD
:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那么該過濾器將被調用,除此之外,該過濾器不會被調用。ERROR
:如果目標資源是通過聲明式異常處理機制調用時,那么該過濾器將被調用。除此之外,過濾器不會被調用。- ASYNC:異步訪問資源時,過濾器被調用
2.2、配置詳解
2.2.1、路徑配置(urlPatterns)
攔截路徑的寫法:
- 攔截所有資源:@WebFilter("/*"),訪問所有資源時,過濾器都會執行
- 攔截目錄:比如@WebFilter("/user/*"),訪問 /user 下的所有資源時,過濾器都會執行
- 后綴名攔截:比如@WebFilter("*.jsp"),訪問所有后綴名為 jsp 的資源時,過濾器都會執行
- 攔截具體資源:比如@WebFilter("/index.jsp")、@WebFilter("/user/servletTest01"),這樣的話只有訪問到該資源時,過濾器才會被執行
2.2.2、攔截方式配置(資源被以什么方式請求時才被攔截)
我們可以配置服務器上的資源被以什么方式被請求時才會被攔截,比如被瀏覽器訪問時、服務器請求轉發等等方式
使用注解時,我們可以通過設置 dispatcherTypes 屬性來配置攔截方式,或者使用 web.xml 配置的<dispatcher>節點來設置。
示例:
@WebFilter(value = "/*", dispatcherTypes = DispatcherType.REQUEST) //注解配置 //同時配置多種方式 @WebFilter(value = "/*", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
該屬性可以設置的值及其意義:
- REQUEST(默認值):當用戶直接訪問頁面時,Web容器將會調用過濾器。如果目標資源是通過RequestDispatcher的include()或forward()方法訪問時,那么該過濾器就不會被調用。
- INCLUDE:如果目標資源是通過RequestDispatcher的include()方法訪問時,那么該過濾器將被調用。除此之外,該過濾器不會被調用。
FORWARD
:如果目標資源是通過RequestDispatcher的forward()方法訪問時,那么該過濾器將被調用,除此之外,該過濾器不會被調用。ERROR
:如果目標資源是通過聲明式異常處理機制調用時,那么該過濾器將被調用。除此之外,過濾器不會被調用。- ASYNC:異步訪問資源時,過濾器被調用
2.3、過濾器鏈(多個過濾器時的執行順序)
我們可以添加多個過濾器,只需定義多個過濾器類即可。
有多個過濾器時,過濾器的執行順序跟使用注解配置還是 web.xml 配置有關。
當使用注解配置時,將會按照字符串的比較規則來比較過濾器的完整類名,值小的先執行。比如 atest.BFilter 和 btest.AFilter,則 atest.BFilter 過濾器先執行。
當使用 web.xml 配置時,哪個過濾器的在 web.xml 里面的定義代碼在上面,哪個就先執行。比如下面配置,BFilter 定義在上面,則該過濾器先執行:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <filter> <filter-name>BFilterName</filter-name> <filter-class>test.BFilter</filter-class> </filter> <filter-mapping> <filter-name>BFilterName</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>AFilterName</filter-name> <filter-class>atest.AFilter</filter-class> </filter> <filter-mapping> <filter-name>AFilterName</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
開始的過濾器執行順序即按照上面所說,但在資源執行后時,執行順序又是開始時的倒序(即在chain.doFilter(req, resp) 后面的語句的執行順序是該語法前面的語句的執行順序的倒序)。比如有兩個過濾器,過濾器1和過濾器2,執行順序如下:
- 過濾器1
- 過濾器2
- 資源執行
- 過濾器2
- 過濾器1