Servlet過濾器

 

過濾器... 1

過濾器基礎知識... 1

Filter工作原理(執行流程)... 2

過濾器的生命週期... 4

Servlet過濾器API 4

過濾器使用案例... 6

解決全站亂碼問題... 6

禁止緩存所有動態頁面的過濾器... 7

控制瀏覽器緩存頁面中的靜態資源的過濾器:... 8


過濾器

過濾器基礎知識

Filter 技術是servlet 2.3 新增加的功能。servlet2.3是sun公司與2000年10月發佈的,它的開發者包括許多個人和公司團體,充分體現了sun公司所倡導的代碼開放性原則。由於衆多的參與者的共同努力,servlet2.3比以往功能都強大了許多,而且性能也有了大幅提高。

Filter 技術使用戶可以改變一個request和修改一個response。 Filter 不是一個servlet,它不能產生一個response,它能夠在一個request到達servlet之前預處理request,也可以在離開servlet時處理response。換種說法,filter其實是一個”servlet chaining”(servlet 鏈)。

 

一個filter 包括:

  1。 在servlet被調用之前截獲;

  2。 在servlet被調用之前檢查servlet request;      如統一的編碼轉換

  3。 根據需要修改request頭和request數據;         裝飾、或者動態代理實現

  4。 根據需要修改response頭和response數據;

5。 在servlet被調用之後截獲。

  可以捕獲servlet運行的結果,比如進行數據壓縮等

以上特點可以通過簡單案例演示,是學生對Filter有一個初步認識。

你能夠配置一個filter 到一個或多個servlet;單個servlet或servlet組能夠被多個filter 使用。幾個實用的filter 包括:用戶辨認filter,日誌filter,審覈filter,加密filter,符號filter,能改變xml內容的XSLT filter等。

一個filter必須實現javax。servlet。Filter接口定義的三個方法: doFilter、init和destroy。(在三個方法在後面後有詳細的介紹)。

 

編寫一個簡單的過濾器對*.jsp文件過濾,演示

chain.doFilter(request, response)

Filter工作原理(執行流程)      

當客戶端發出Web資源的請求時,Web服務器根據應用程序配置文件設置的過濾規則進行檢查,若客戶請求滿足過濾規則,則對客戶請求/響應進行攔截,對請求頭和請求數據進行檢查或改動,並依次通過過濾器鏈,最後把請求/響應交給請求的Web資源處理。請求信息在過濾器鏈中可以被修改,也可以根據條件讓請求不發往資源處理器,並直接向客戶機發回一個響應。當資源處理器完成了對資源的處理後,響應信息將逐級逆向返回。同樣在這個過程中,用戶可以修改響應信息,從而完成一定的任務。

    在這裏,我要插幾句——關於過濾鏈的問題:上面說了,當一個請求符合某個過濾器的過濾條件時該請求就會交給這個過濾器去處理。那麼當兩個過濾器同時過濾一個請求時誰先誰後呢?這就涉及到了過濾鏈FilterChain。

所有的奧祕都在Filter的FilterChain中。服務器會按照web.xml中過濾器定義的先後循序組裝成一條鏈,然後一次執行其中的doFilter()方法。(注:這一點Filter和Servlet是不一樣的,具體請參看我的另一篇文章:Servlet和Filter映射匹配原則之異同)執行的順序就如下圖所示,執行第一個過濾器的chain.doFilter()之前的代碼,第二個過濾器的chain.doFilter()之前的代碼,請求的資源,第二個過濾器的chain.doFilter()之後的代碼,第一個過濾器的chain.doFilter()之後的代碼,最後返回響應。

      
    這裏還有一點想補充:大家有沒有想過,上面說的“執行請求的資源”究竟是怎麼執行的?對於“執行第一個過濾器的chain.doFilter()之前的代碼,第二個過濾器的chain.doFilter()之前的代碼”這些我可以理解,無非就是按順序執行一句句的代碼,但對於這個“執行請求的資源”我剛開始卻是怎麼也想不明白。直到我見到上面這張圖片才恍然大悟。其實是這樣的:

    通常我們所訪問的資源是一個servlet或jsp頁面,而jsp其實是一個被封裝了的servlet,於是我們就可以統一地認爲我們每次訪問的都是一個Servlet,而每當我們訪問一個servlet時,web容器都會調用該Servlet的service方法去處理請求。而在service方法又會根據請求方式的不同(Get/Post)去調用相應的doGet()或doPost()方法,實際處理請求的就是這個doGet或doPost方法。寫過servlet的朋友都應該知道,我們在doGet(或doPost)方法中是通過response.getWriter()得到客戶端的輸出流對象,然後用此對象對客戶進行響應。

到這裏我們就應該理解了過濾器的執行流程了:

執行第一個過濾器的chain.doFilter()之前的代碼

——>第二個過濾器的chain.doFilter()之前的代碼

——>……

——>第n個過濾器的chain.doFilter()之前的代碼

——>所請求servlet的service()方法中的代碼

——>所請求servlet的doGet()或doPost()方法中的代碼

——>第n個過濾器的chain.doFilter()之後的代碼

——>……

——>第二個過濾器的chain.doFilter()之後的代碼

——>第一個過濾器的chain.doFilter()之後的代碼。

 

 

簡單演示多個過濾器共同作用的過程

FilterDemo1

System.out.println("11111111 before");

chain.doFilter(request, response);

System.out.println("1111111111 after");

 

FilterDemo2

System.out.println("22222222 before");

chain.doFilter(request, response);

System.out.println("22222222 after");

 

Index.jsp

<% System.out.println("jsp xxxxxxxxxxxxxxx............"); %>

輸出結果

11111111 before

222222222222 before

jsp xxxxxxxxxxxxxxx............

222222222222 after

1111111111 after

 

交換web.xml中兩個過濾器的描述代碼位置,重新運行,結果爲:

222222222222 before

11111111 before

jsp xxxxxxxxxxxxxxx............

1111111111 after

222222222222 after

 

過濾器的生命週期

過濾器的生命週期:(一定要實現javax.servlet包的Filter接口的三個方法init()、doFilter()、destroy(),空實現也行)
(1)、啓動服務器時加載過濾器的實例,並調用init()方法來初始化實例;
(2)、每一次請求時都只調用方法doFilter()進行處理;
(3)、停止服務器時調用destroy()方法,銷燬實例。

 

案例:在三個方法中編寫打印語句,然後啓動、關閉服務器演示生命週期。

 

Servlet過濾器API

Servlet過濾器API包含了3個接口,它們都在javax.servlet包中,分別是Filter接口、FilterChain接口和FilterConfig接口。

1  public Interface Filter
    所有的過濾器都必須實現Filter接口。該接口定義了init,doFilter0,destory()三個方法:
    (1) public void init (FilterConfig filterConfig) throws   ServletException.
    當開始使用servlet過濾器服務時,Web容器調用此方法一次,爲服務準備過濾器;然後在需要使用過濾器的時候調用doFilter(),傳送給此方法的FilterConfig對象,包含servlet過濾器的初始化參數。
    (2)public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws java.io.IOException,ServletException.

每個過濾器都接受當前的請求和響應,且FilterChain過濾器鏈中的過濾器(應該都是符合條件的)都會被執行。doFilter方 法中,過濾器可以對請求和響應做它想做的一切,通過調用他們的方法收集數據,或者給對象添加新的行爲。過濾器通過傳送至此方法的FilterChain參數,調用chain.doFilterO將控制權傳送給下一個過濾器。當這個調用返回後,過濾器可以在它的 Filter方法的最後對響應做些其他的工作。如果過濾器想要終止請求的處理或得到對響應的完全控制,則可以不調用下一個過濾器,而將其重定向至其它一些頁面。當鏈中的最後一個過濾器調用chain.doFilterO方法時,將運行最初請求的Servlet。

(3)public void destroy()
     一旦doFilterO方法裏的所有線程退出或已超時,容器調用此方法。服務器調用destoryO以指出過濾器已結束服務,用於釋放過濾器佔用的資源。

 

2 public interface FilterChain

public void doFilter(ServletRequest request,ServletResponse response)

thlows java.io.IOException,ServletException

此方法是由Servlet容器提供給開發者的,用於對資源請求過濾鏈的依次調用,通過FilterChain調用過濾鏈中的下一個過濾   器,如果是最後一個過濾器,則下一個就調用目標資源。

3 public interface FilterConfig

     FilterConfig接口檢索過濾器名、初始化參數以及活動的Servlet上下文。該接口提供了以下4個方法:

     (1)public java.1ang.String getFilterName0
           返回web.xml部署文件中定義的該過濾器的名稱。
     (2)public ServletContext getServletContextO
          返回調用者所處的servlet上下文。
     (3)public java.1ang.String getlnitParameter(java.1ang.String name)
          返回過濾器初始化參數值的字符串形式,當參數不存在時,返回nul1.name是初始化參數名。
     (4)public java.util.Enumeration getlnitParameterNames()
          以Enumeration形式返回過濾器所有初始化參數值,如果沒有初始化參數,返回爲空。

 

過濾器相關接口工作流程

從編程的角度看,過濾器類將實現Filter接口,然後使用這個過濾器類中的FilterChain和FilterConfig接口。該過濾器類的

— 個引用將傳遞給FilterChain對象,以允許過濾器把控制權傳遞給鏈中的下一個資源。FilterConfig對象將由容器提供給過濾
器,以允許訪問該過濾器的初始化數據。詳細流程如下圖所示:

 

過濾器配置

   過濾器通過Web應用程序中的配置描述符web.xml文件中的明,包括部分:

過濾器定義,由<filter>元素表示,主要包括<filter-name>和<filter-class>兩個必須的子元素和<icon>、<init-param>,<display-name>,<description>這4個可選的子元素。<filter-name>子元素定義了—個過濾器的名字,<filter-class>指定了由容器載入的實際類,<init-param>子元素爲過濾器提供初始化參數。

<filter-mapping> 主要由<filter-name>,<servlet-name>和<url-pattem>子元素組成。<servlet-name>將過濾器映射到一個或多個Servlet上,<url-pattem>將過濾器映射到—個或多個任意特徵的URL的JSP頁面。


過濾器使用案例

解決全站亂碼問題

CharseterEncodingFilter1.java

private FilterConfig config = null;

private String defaultCharset = "utf-8";

 

public void doFilter(ServletRequest request, ServletResponse response,

           FilterChain chain) throws IOException, ServletException {

      

       HttpServletRequest req = (HttpServletRequest) request;

       HttpServletResponse resp = (HttpServletResponse) response;

      

       String charset = config.getInitParameter("charset");

       if(charset==null){

           charset = defaultCharset;

       }

       request.setCharacterEncoding(charset);

 

        //有時候response不用設置編碼,因爲servlet一般不做輸出,

//輸出交由jsp來做,所以只要jsp頁面設定編碼即可

       resp.setCharacterEncoding(charset);

       resp.setContentType("text/html;charset="+charset);

      

       chain.doFilter(req, resp);

 

}

 

public void init(FilterConfig filterConfig) throws ServletException {

       this.config = filterConfig;

}

Web.xml

    <filter>

       <filter-name>charseterEncodingFilter1</filter-name>

        <filter-class>cn.class3g.web.filter.CharseterEncodingFilter1</filter-class>

    </filter>

    <filter-mapping>

       <filter-name>charseterEncodingFilter1</filter-name>

       <url-pattern>/*</url-pattern>

    </filter-mapping>

 

Index.jsp

   <form action="/Filter_Test/servlet/TestCharsetServlet" method="post" >

   城市:<input type="text" name="city" value="保定"/> <br/>

   <input type="submit" value="提交" />

   </form>

TestCharsetServlet.java

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

    String city = request.getParameter("city");

    response.getWriter().write(city);

}

public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

    String city = request.getParameter("city");

    response.getWriter().write(city);

}

 

l         運行測試結果,頁面能夠正常顯示

l         將過濾器去掉,再次測試,亂碼

l         將表單的method改爲get再次測試,亂碼。因爲此種辦法不能解決get方式的亂碼問題

 

禁止緩存所有動態頁面的過濾器

a)       有 3 個 HTTP 響應頭字段都可以禁止瀏覽器緩存當前頁面,它們在 Servlet 中的示例代碼如下:

l         response.setDateHeader("Expires",-1);

l         response.setHeader("Cache-Control","no-cache"); 

l         response.setHeader("Pragma","no-cache"); 

b)       並不是所有的瀏覽器都能完全支持上面的三個響應頭,因此最好是同時使用上面的三個響應頭。

c)       Expires數據頭:值爲GMT時間值,爲-1指瀏覽器不要緩存頁面

d)       Cache-Control響應頭有兩個常用值:

l         no-cache指瀏覽器不要緩存當前頁面。

l         max-age:xxx指瀏覽器緩存頁面xxx秒

NoCacheFilter

    public void doFilter(ServletRequest arg0, ServletResponse arg1,

           FilterChain arg2) throws IOException, ServletException {

       HttpServletRequest request = (HttpServletRequest) arg0;

       HttpServletResponse response = (HttpServletResponse) arg1;

      

       response.setDateHeader("expires",-1);

       response.setHeader("Cache-Control","no-cache");

       response.setHeader("Pragma","no-cache");

      

       arg2.doFilter(request, response); 

    }

Web.xml

    <filter>

       <filter-name>NoCacheFilter</filter-name>

        <filter-class>cn.class3g.web.filter.NoCacheFilter</filter-class>

    </filter>

    <filter-mapping>

       <filter-name>NoCacheFilter</filter-name>

       <url-pattern>*.jsp</url-pattern>

    </filter-mapping>

    <filter-mapping>

       <filter-name>NoCacheFilter</filter-name>

       <url-pattern>/servlet/*</url-pattern>

    </filter-mapping>

可以設置多個filter-mapping來映射不同的文件類型

測試:

提交表單數據,返回,查看錶單中的數據是否仍然存在

 

控制瀏覽器緩存頁面中的靜態資源的過濾器:

場景:有些動態頁面中引用了一些圖片或css文件以修飾頁面效果,這些圖片和css文件經常是不變化的,所以爲減輕服務器的壓力,可以使用filter控制瀏覽器緩存這些文件,以提升服務器的性能。

ExpiresFilter

private FilterConfig config;

public void doFilter(ServletRequest req, ServletResponse resp,

           FilterChain chain) throws IOException, ServletException {

      

       HttpServletRequest request = (HttpServletRequest) req;

       HttpServletResponse response = (HttpServletResponse) resp;

      

       //1.得到請求的是什麼資源  css js  jpg

       String uri = request.getRequestURI();

       if(uri.endsWith(".css")){

           long expriesTime = Integer.parseInt(this.config.getInitParameter("css"))*1000;

           response.setDateHeader("expires", System.currentTimeMillis()+expriesTime);

       }else if(uri.endsWith(".js")){

           long expriesTime = Integer.parseInt(this.config.getInitParameter("js"))*1000;

           response.setDateHeader("expires", System.currentTimeMillis()+expriesTime);

       }else if(uri.endsWith(".jpg")){

           long expriesTime = Integer.parseInt(this.config.getInitParameter("jpg"))*1000;

           response.setDateHeader("expires", System.currentTimeMillis()+expriesTime);

       }

       chain.doFilter(request, response);

}

 

public void init(FilterConfig filterConfig) throws ServletException {

       this.config = filterConfig;

}

Web.xml

    <filter>

       <filter-name>ExpiresFilter</filter-name>

        <filter-class>cn.class3g.web.filter.ExpiresFilter</filter-class>

       <init-param>

           <param-name>css</param-name>

           <param-value>120</param-value>

       </init-param>

       <init-param>

           <param-name>js</param-name>

           <param-value>120</param-value>

       </init-param>

       <init-param>

           <param-name>jpg</param-name>

           <param-value>120</param-value>

       </init-param>

    </filter>

    <filter-mapping>

       <filter-name>ExpiresFilter</filter-name>

       <url-pattern>*.css</url-pattern>

    </filter-mapping>

    <filter-mapping>

       <filter-name>ExpiresFilter</filter-name>

       <url-pattern>*.js</url-pattern>

    </filter-mapping>

    <filter-mapping>

       <filter-name>ExpiresFilter</filter-name>

       <url-pattern>*.jpg</url-pattern>

    </filter-mapping>

測試流程

複製一張圖片tss.jpg放到webroot下

Index.jsp添加代碼如下

<img src="tss.jpg" />

 

打開ie瀏覽器臨時文件夾,清空臨時文件夾。一般爲:

C:\Documents and Settings\Administrator\Local Settings\Temporary Internet Files

打開瀏覽器,運行httpwatch,訪問index.jsp,

結果:如果過濾器正常工作,在緩存的文件沒有超時的時間內,儘管多次訪問tss.jpg也只會向服務器發送一次請求。

同時觀察臨時文件的產生情況和httpwatch中對於tss.jpg的請求次數

注意,如果用刷新則一定會發生重新請求,不管文件是否已被緩存

發佈了70 篇原創文章 · 獲贊 31 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章