本篇我們討論過濾器(Filter)。過濾器就是在來源資料和目的資料之間起過濾作用的中間組件。過濾器可以截取用戶端和資源之間的請求與響應訊息,並對這些訊息進行過濾。
當web容器接收到一個對資源的請求時,它先判斷是否有過濾器與這個資源相連,如果由,容器會把請求交給過濾器處理。在過濾器中,你可以改變請求的內容,或者重新佈建要求的前序資訊等。當目標資源對請求作出響應時,容器同樣會將響應先轉寄給過濾器,在過濾器中,你可以對響應的內容進行轉換,然後再將響應發送到用戶端。過濾器對用戶端和目標資源來說是透明的。
可以在web應用程式中部署多個過濾器,組成過濾器鏈。過濾器鏈中的每個過濾器負責特定的操作和任務。過濾器的主要應用如下:
對使用者請求進行統一認證
對使用者的請求進行記錄和審核
對使用者發送的資料進行過濾和轉換
轉換映像格式
對響應內容進行壓縮,以減少傳輸量
對請求和響應進行加密處理
觸發資源訪問事件
Filter API
和過濾器開發相關的介面與類都包含在javax.servlet和javax.servlet.http包中,主要有以下介面和類。
javax.servlet.Filter//介面javax.servlet.FilterConfig//介面javax.servlet.FilterChain//介面javax.servlet.ServletRequestWrapper//類javax.servlet.ServletResponseWrapper//類javax.servlet.http.HttpServletRequestWrapper//類javax.servlet.http.HttpServletResponseWrapper//類
我們先介紹三個主要的介面。
Filter介面
開發過濾器要實現Filter介面。Filter介面定義了三個方法。
1)init(),web容器調用該方法來初始化過濾器。容器在調用該方法時,向過濾器傳遞FilterConfig對象,FilterConfig用法和ServletConfig相似。利用FilterConfig可以得到ServletContext對象,以及在部署描述檔案中配置的過濾器的初始化參數。
2)doFilter,該方法類似於servlet的Service()方法。當用戶端請求目標資源時,容器就會調用與這個目標資源管理的過濾器的doFilter()方法。在這個方法中,可以對請求和響應進行處理,處理完後,也可以調用chain.doFilter(request,response)將請求傳遞給下一個過濾器,也可以直接傳回用戶端。
3)destroy(),web容器調用該方法指示過濾器的生命週期結束。在這個方法中,可以釋放過濾器使用的資源。
與開發servlet不同的是,Filter介面並沒有相應的類可供繼承,要開發過濾器,只能實現Filter介面。
FilterConfig介面
該介面由容器實現,類似於servlet的ServletConfig介面,用於在過濾器初始化時向其傳遞資訊,在該介面中,定義了一下4個方法。
1)getFilterName(),得到在部署描述檔案中指定的過濾器的名字
2)getInitParameter(String name),得到在部署描述檔案中指定的名字為name的初始化參數的值
3)getInitParameterNames(),返回過濾器的所有初始化參數的名字的枚舉集合。如果沒有初始化參數,則返回空值。
4)getServletContext(),返回servlet內容物件的引用。
FilterChain介面
該介面由容器實現,容器將其執行個體作為參數傳入過濾器對象的doFilter()方法中。過濾器對象使用FilterChain對象調用過濾器鏈中的下一個過濾器,如果該過濾器是過濾器鏈的最後一個過濾器,那麼將調用目標資源。它只有一個方法:
doFilter(request,response),調用該方法將使過濾鏈中的下一個過濾器被調用。如果調用該方法的過濾器是鏈中最後一個,那麼目標資源將被調用。
過濾器的部署
在實現一個過濾器後,需要在部署描述檔案中對過濾器進行配置。配置通過<filter>和<filter-mapping>來完成。一個配置描述檔案的樣本如下:
<?xml version='1.0' encoding='utf-8'?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"><filter><filter-name>TestFilter</filter-name><filter-class>com.shan.filter.TestFilter</filter-class><init-param><param-name>word</param-name><param-value>/WEB-INF/word.txt</param-value></init-param></filter><filter-mapping><filter-name>TestFilter</filter-name><url-pattern>/test.jsp</url-pattern><servlet-name>servlet1</servlet-name><servlet-name>*</servlet-name><dispatcher>REQUEST</dispatcher></filter-mapping></web-app>
其中,<url-pattern>元素指定過濾器關聯的URL樣式,<servlet-name>指定過濾器對應的servlet。如果<filter-mapping>該值為*,表示對於任意的servlet。<filter-mapping>元素還可以包含最多4個<dispatcher>元素。該元素指定過濾器對應的請求方式,可以是REQUEST,INCLUDE,FORWARD,ERROR中的一個。預設是REQUEST。
現在我們來看一個過濾器的Demo程式。項目建立步驟和之前的完全相同,在此不再贅述。
編寫過濾器類
package com.shan.filter;import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class TestFilter implements Filter {public void init(FilterConfig config) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {response.setContentType("text/html;charset=gb2312");PrintWriter out = response.getWriter();out.println("Before doFilter()");chain.doFilter(request,response);out.println("After doFilter()");out.close();}public void destroy() {}}
編寫測試頁面
<%@ page contentType="text/html;charset=gb2312" %>This is a test page..
編譯和部署過濾器
<?xml version='1.0' encoding='utf-8'?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"><filter><filter-name>TestFilter</filter-name><filter-class>com.shan.filter.TestFilter</filter-class></filter><filter-mapping><filter-name>TestFilter</filter-name><url-pattern>/test.jsp</url-pattern></filter-mapping></web-app>
在dos環境下切換到工程所在目錄,執行如下語句,對TestFilter類進行編譯
javac -classpath D:\apache-tomcat-7.0.33\lib\servlet-api.jar;classes -d classes src\com\shan\filter\TestFilter.java
配置程式
在D:\apache-tomcat-7.0.33\webapps檔案夾下建立TestFilter檔案夾,在TestFilter檔案夾下建立WEB-INF目錄,將剛才編譯後的classes檔案夾拷貝到WEB-INF目錄下。將該才建立的test.jsp拷貝到TestFilter檔案夾下。
運行結果
在瀏覽器網址欄輸入localhost:8080/TestFilter/test.jsp,結果如下:
可以看到,在輸出This is a test之前,先執行了doFilter中輸出語句,輸出Before doFilter(),然後調用chain.doFilter(request,response)將請求傳遞給下一個過濾器,因為這之後沒有其他過濾器,所以傳遞給目標資源,輸出This is a test,在響應時又調用了doFilter語句,輸出After doFilter()。
對請求和響應資料進行過濾
在留言板程式中,如果使用者輸入非法字串,則應該對這些字串進行屏蔽。不過HttpServletRequset類並沒有提供對請求資訊進行修改的setXXX()方法,而HttpServletResponse類也沒有提供擷取響應資料的方法,所以雖然過濾器可以截取到請求和響應對象,但是卻無法直接使用這兩個對象對他們的資料繼續替換。不過,在servlet規範中,定義了4個封裝類:ServletRequsetWrapper,ServletResponseWrapper,HttpServletRequsetWrapper,HttpServletReponseWrapper。這4個封裝類分別實現了請求或響應的介面。它們在構造方法中接收真正的請求或響應對象,然後利用該對象的方法來完成自己需要實現的方法。
有了封裝類,要修改請求或響應資訊,就只需要編寫一個封裝類的子類,然後顧飛想要修改的方法即可。例如,我們想要為所有請求添加一個查詢字串,可以編寫一個HttpServletRequestWrapper類的子類,並重寫getQueryString()方法,然後在過濾器的doFilter()方法中,構造這個子類的對象,將其作為參數傳遞給chain.doFilter()方法即可。
對響應內容進行壓縮的過濾器
網站的訪問速度由很多因素決定,包括伺服器效能,網路頻寬,web程式響應速度,伺服器與用戶端網路傳輸速度等。從軟體角度講,我們要提高網站訪問速度,即要儘可能提高web應用程式的執行速度,這可以通過最佳化代碼來實現。如果要進一步提升網頁瀏覽速度,那可以對響應內容進行壓縮,以節省網路的頻寬,提升訪問速度。
目前主流瀏覽器都支援壓縮後的網頁內容,包括gzip,deflate壓縮方式。瀏覽器和web伺服器對於壓縮網頁的通訊過程如下:
1)如果瀏覽器能接受壓縮後的網頁內容,那麼它會在請求中發送Accept-Encoding前序,設定內容為gzip等。
2)web伺服器讀取Accept-Encoding請求前序的值來判斷瀏覽器是否接受壓縮,如果接受,則將目標頁面響應內容進行壓縮後再發送到用戶端,同時設定Content-Encoding實體前序。
3)瀏覽器接收到響應內容後,按照Content-Encoding中的值對響應內容進行解壓縮,然後顯示。
我們可以通過過濾器來對目標頁面的響應內容進行壓縮,該過程類似於對請求和響應內容進行過濾的過濾器。實現原理就是使用封裝類對象替換原始的響應對象,並使用java.util.zip.GZIPOutputStream作為響應內容的輸出資料流對象。
轉載請註明出處:http://blog.csdn.net/iAm333