Java Filter過濾機制詳解

以前以爲Filter只是一個特殊點的Servlet,用進只需在web.xml中配置一下攔截路徑就可以了,可經過這兩天的深入學習,才知道以前對Filter類的瞭解和使用都太過淺薄了。

在網上看了很多篇相關的技術文章,現提取它們中的精髓(我認爲能說明問題的東西)及我個人的一些學習經驗作如下總結,希望能幫助大家更好地理解Filter的過濾機制。

要學習Filter,就必須先理解和掌握servlet的調用機制和流程。關於servlet,網上也有很多相關文章,大家若有不明白的可以到百度中搜一下,我這裏爲了突出重點就不多說了。好,下面進入正題。

一、什麼是Filter

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

  它新增加的功能包括:

  1. 應用程序生命週期事件控制;

  2. 新的國際化;

  3. 澄清了類的裝載規則;

  4. 新的錯誤及安全屬性;

  5. 不贊成使用HttpUtils 類;

  6. 各種有用的方法;

  7. 闡明並擴展了幾個servlet DTD;

  8. filter功能.

  其中最重要的就是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被調用之後截獲.

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

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

二、Filter體系結構

        2.1、Filter工作原理(執行流程)      

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

************************************************************************************************************************

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

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

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

        通常我們所訪問的資源是一個servlet或jsp頁面,而jsp其實是一個被封裝了的servlet(每個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()之後的代碼。

       以上是我的理解,若有不對之處,還有大家不吝指教啊,大家一起學習!!

************************************************************************************************************************

 

2.2、 Servlet過濾器API
          Servlet過濾器API包含了3個接口,它們都在javax.servlet包中,分別是Filter接口、FilterChain接口和FilterConfig接口。
       2.2.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.2.2 public interface FilterChain
      public void doFilter(ServletRequest request,ServletResponse response)
    thlows java.io.IOException,ServletException
      此方法是由Servlet容器提供給開發者的,用於對資源請求過濾鏈的依次調用,通過FilterChain調用過濾鏈中的下一個過濾   器,如果是最後一個過濾器,則下一個就調用目標資源。


   2.2.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形式返回過濾器所有初始化參數值,如果沒有初始化參數,返回爲空。

   2.3、過濾器相關接口工作流程
   從編程的角度看,過濾器類將實現Filter接口,然後使用這個過濾器類中的FilterChain和FilterConfig接口。該過濾器類的
— 個引用將傳遞給FilterChain對象,以允許過濾器把控制權傳遞給鏈中的下一個資源。FilterConfig對象將由容器提供給過濾
器,以允許訪問該過濾器的初始化數據。詳細流程如下圖所示:

2.4、過濾器配置
    過濾器通過Web應用程序中的配置描述符web.xml文件中的明,包括部分:過濾器定義,由<filter>
元素表示,主要包括<filter-name>和<f'flter-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頁面。

三、應用實例
          從上面分析可知,實現Servlet過濾器,需要兩步:第一步開發過濾器,設計—個實現Fiker接口的類;第二步通過web.xml配置過濾器,實現過濾器和Servlet、JSP頁面之間的映射。以下設計一個簡單的IP地址過濾器,根據用戶的IP地址進行對網站的訪問控制。
(1)過濾器的設計ipfilter.java
package ipf;
imp0rt java.io.IOException;
imp0rt javax.servlet.*;
public class ipfilter implements Filter//實現Filter接口
{protected FilterConfig config;
protected String rejectedlP;
public void init(FilterConfig filterConfig)throws
ServletException
{this.config=filterConfig;//從Web)lE務器獲取過濾器配置對象
rejectedlP=config.getlnitParameter( RejectedlP”):
,,從配置中取得過濾lP
if(rejectedlP=:nul1)‘rejectedlP= )
)
public void doFilter(ServletRequest request,
ServletResponse response.FilterChain chain)throws
IOException,ServletException
{RequestDispatcher dispatcher=request.getRequestDispatcher("");
String remotelP=request.getRemoteAddrO;//獲取客戶請求lP
int i=remotelP.1astlndexOf(".");
int r=rejectedlP.1astlndexOf(”.”):
String relPscope=rejectedlP.substring(0,r);//過濾lP段
if(relPscope.equals(remotelP.substring(O.i)))
{      dispatcher.forward(request,response);//重定向到rejectedError.jsp頁面
        retum;//阻塞,直接返Web回客戶端
}
else{chain.doFilter(request,response);//調用過濾鏈上的下一個過濾器
}
}
public void destroy()
       //過濾器功能完成後,由Web服務器調用執行,回收過濾器資源
注意:chain.doFilterO語句以前的代碼用於對客戶請求的處理;以後的代碼用於對響應進行處理。
(2)配置過濾器
    在應用程序Web—INF目錄下的web.xml描述符文件中添加以下代碼:

<filter>
<filter-name>ipfIter</filter-name>//過濾器名稱
<filter-class>ipf.ipfiIter</filter-class>//實現過濾器的類
<init—param>
<param—name>RejectedlP</param-name>//過濾器初始化參數名RejectedlP
<param-value>192.168.12.*/param-value>
</init—pamm>
</filter>
<filter-mapping>//過濾器映射(規律規則)
<filter-name>ipfiIter</filter-name>
<url—pattem>/*</ud-pattem>
//映射到Web應用根目錄下的所有JSP文件
</filter-mapping>
通過以上設計與配置,就禁止了IP地址處在192.168.12網段的用戶對網站的訪問。

四、 結束語
Servlet過濾器功能強大,應用廣泛,除支持Servlet和JSP頁面的基本功能,比如13志記錄、性能、安全、會話處理、XSLT轉換等外,在J2EE應用程序中使用JavaServlet過濾器轉換其輸出,以便兼容任何類型客戶端也表現出了很好的前景。Servlet過濾器能夠偵測到來自使用WAP協議(無線應用協議)的移動客戶端的呼叫,並且將答覆內容轉換成WML(無線標記語言)格式。Servlet過濾器也能檢測到來自iMode無線客戶的呼叫,並將其轉變成cHTML(緊湊HTML)格式等等。因而,深刻理解Servlet過濾器的工作機制,熟練掌握編程技術,在實際的開發過程中,可以不斷地發現新的用途,增強組件的可重用性,提高Web應用程序的可維護性。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章