Filter、FilterChain、FilterConfig

1、Filter的基本工做原理html

一、Filter程序是一個實現了特殊接口的Java類,與Servlet相似,也是由Servlet容器進行調用和執行的。java

二、當在web.xml註冊了一個Filter來對某個Servlet程序進行攔截處理時,它能夠決定是否將請求繼續傳遞給Servlet程序,以及對請求和響應消息是否進行修改。web

三、當Servlet容器開始調用某個Servlet程序時,若是發現已經註冊了一個Filter程序來對該Servlet進行攔截,那麼容器再也不直接調用Servlet的service方法,而是調用Filter的doFilter方法,再由doFilter方法決定是否去激活service方法。數據庫

四、但在Filter.doFilter方法中不能直接調用Servlet的service方法,而是調用FilterChain.doFilter方法來激活目標Servlet的service方法,FilterChain對象時經過Filter.doFilter方法的參數傳遞進來的。瀏覽器

五、只要在Filter.doFilter方法中調用FilterChain.doFilter方法的語句先後增長某些程序代碼,這樣就能夠在Servlet進行響應先後實現某些特殊功能。緩存

六、若是在Filter.doFilter方法中沒有調用FilterChain.doFilter方法,則目標Servlet的service方法不會被執行,這樣經過Filter就能夠阻止某些非法的訪問請求。服務器

2、Filter鏈

一、在一個Web應用程序中能夠註冊多個Filter程序,每一個Filter程序均可以對一個或一組Servlet程序進行攔截。若是有多個Filter程序均可以對某個Servlet程序的訪問過程進行攔截,當針對該Servlet的訪問請求到達時,Web容器將把這多個Filter程序組合成一個Filter鏈(也叫過濾器鏈)。app

二、Filter鏈中的各個Filter的攔截順序與它們在web.xml文件中的映射順序一致,上一個Filter.doFilter方法中調用FilterChain.doFilter方法將激活下一個Filter的doFilter方法,最後一個Filter.doFilter方法中調用的FilterChain.doFilter方法將激活目標Servlet的service方法。ide

三、只要Filter鏈中任意一個Filter沒有調用FilterChain.doFilter方法,則目標Servlet的service方法都不會被執行。this

3、Filter接口

一個Filter程序就是一個Java類,這個類必須實現Filter接口。javax.servlet.Filter接口中定義了三個方法:init、doFilter、destory。

一、init方法

(1)、在Web應用程序啓動時,Web服務器(Web容器)將根據其web.xml文件的配置信息來建立每一個註冊的Filter的實例對象,並將其保存在內存中。

(2)、Web容器建立Filter的實例對象後,將當即調用該Filter對象的init方法。init方法在Filter生命週期中僅被執行一次,Web容器在調用init方法時,會傳遞一個包含Filter的配置和運行環境信息的FilterConfig對象。

public voic init(FilterConfig filterConfig) throws ServletException

(3)開發人員能夠在init方法中完成與構造方法相似的初始化功能,要注意的是:若是初始化代碼要使用到FilterConfig對象,這些代碼只能在init方法中編寫,而不能在構造方法中編寫(還沒有調用init方法,即並無建立FilterConfig對象,要使用它則必然出錯)。

二、doFilter方法

當一個Filter對象可以攔截訪問請求時,Servlet容器將調用Filter對象的doFilter方法。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException.ServletException

其中,參數request和response爲Web容器或Filter鏈中上一個Filter傳遞過來的請求和響應對象;參數chain爲表明當前Filter鏈的對象

三、destroy方法

該方法在Web容器卸載Filter對象以前被調用,也僅執行一次。能夠完成與init方法相反的功能,釋放被該Filter對象打開的資源,例如:關閉數據庫鏈接和IO流。

4、FilterChain接口

該接口用於定義一個Filter鏈的對象應該對外提供的方法,這個接口只定義了一個doFilter方法。

public void doFilter(ServletRequest request, ServletResponse response) throws java.io.IOException.ServletException

FilterChain接口的doFilter方法用於通知Web容器把請求交給Filter鏈中的下一個Filter去處理,若是當前調用此方法的Filter對象是Filter鏈中的最後一個Filter,那麼將把請求交給目標Servlet程序去處理。

5、FilterConfig接口

一、與普通的Servlet程序同樣,Filter程序也極可能須要訪問Servlet容器。Servlet規範將表明ServletContext對象和Filter的配置參數信息都封裝到一個稱爲FilterConfig的對象中。

二、FilterConfig接口則用於定義FilterConfig對象應該對外提供的方法,以便在Filter程序中能夠調用這些方法來獲取ServletContext對象,以及獲取在web.xml文件中爲Filter設置的友好名稱和初始化參數。

三、FilterConfig接口定義的各個方法:

  • getFilterName方法,返回<filter-name>元素的設置值。

  • getServletContext方法,返回FilterConfig對象中所包裝的ServletContext對象的引用。

  • getInitParameter方法,用於返回在web.xml文件中爲Filter所設置的某個名稱的初始化的參數值。

  • getInitParameterNames方法,返回一個Enumeration集合對象。

6、Filter的註冊與映射

一、註冊Filter

一個<filter>元素用於註冊一個Filter。其中,<filter-name>元素是必需的,<filter-class>元素也是必需的,<init-param>元素是可選的,能夠有多個<init-param>元素。

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>FirstFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>GB2312</param-value>
    </init-param>
</filter>

二、映射Filter

<filter-mapping>元素用於設置一個Filter所負責攔截的資源。一個Filter攔截的資源能夠經過兩種方式來指定:資源的訪問請求路徑和Servlet名稱。

第一種:指定資源的訪問路徑

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<url-pattern>元素中的訪問路徑的設置方式遵循Servlet的URL映射規範。http://my.oschina.net/u/1171518/blog/220147。eg:

/*:表示攔截全部的訪問請求

/filter/*:表示攔截filter目錄下的全部訪問請求,如:http://localhost:8888/testFilter_001/filter/xxxxxx

/test.html:表示攔截根目錄下以test.html爲資源名的訪問請求,訪問連接只會是:http://localhost:8888/test.html

第二種:指定Servlet的名稱

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <servlet-name>default></servlet-name>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

(1)、<servlet-name>元素與<url-pattern>元素是二選一的關係,其值是某個Servlet在web.xml文件中的註冊名稱。

(2)、<dispatcher>元素的設置值有4種:REQUEST、INCLUDE、FORWARD、ERROR,分別對應Servlet容器調用資源的4種方式:

  • 經過正常的訪問請求調用;

  • 經過RequestDispatcher.include方法調用;

  • 經過RequestDispatcher.forward方法調用;

  • 做爲錯誤響應資源調用。

若是沒有設置<dispatcher>子元素,則等效於REQUEST的狀況。也能夠設置多個<dispatcher>子元素,用於指定Filter對資源的多種調用方式都進行攔截。

7、Filter程序示例

一、

FitstFilter.java

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
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 FirstFilter implements Filter {
    private FilterConfig filterConfig = null;
    String paramValue = null;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        paramValue = filterConfig.getInitParameter("encoding");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        System.out.println("begin headers-------------------");
        Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();
        
        while(headerNames.hasMoreElements()) {
            String headerName = (String)headerNames.nextElement();
            System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
        }
        System.out.println("end headers-------------------");
        
        //在調用目標前寫入響應內容
        response.setContentType("text/html; charset=gb2312");
        PrintWriter out = response.getWriter();
        out.println("IP地址爲:" + request.getRemoteHost() + "<br>");

        chain.doFilter(request, response);
        
        //在目標返回後寫入響應內容
        out.println("<br>名稱爲encoding的初始化參數的值爲:" + paramValue);
        out.println("<br>當前Web程序的真實路徑爲:" + filterConfig.getServletContext().getRealPath("/"));
        
        //out.println("<br>修改了test.html文件!");
    }
    
    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

web.xml

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>FirstFilter</filter-class>
    <init-param>
	<param-name>encoding</param-name>
	<param-value>GB2312</param-value>
    </init-param>
</filter>
	
<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

test.html(位於WebContent路徑的filter目錄中)

<html>
<head>
<meta charset="GB2312">
<title>Insert title here</title>
</head>
<body>
這就是test.html頁面的原始內容!
</body>
</html>

訪問:http://localhost:8888/testFilter_001/filter/test.html

瀏覽器顯示頁面:

Tomcat後臺(第一次訪問):

二、

把FirstFilter.java中以下語句的註釋去掉:(整個過程,並未特地或無心改變了test.html文件的內容)

out.println("<br>修改了test.html文件!");

等Tomcat Reload完成,刷新頁面,驚奇地發現瀏覽器頁面並未發生改變,Tomcat後天多了三個請求頭:

緣由(存在疑問,是否真的是If-Modified-Since標籤來決定是否採用緩存):

(1)、緩存裏存儲的不僅是網頁文件,還有服務器發過來的該文件的最後服務器修改時間;

(2)、客戶端發HTTP請求時,使用If-Modified-Since標籤,把上次服務器告訴它的文件最後修改時間返回到服務器端了;

(3)、服務器會把這個時間與服務器上實際文件的最後修改時間進行比較;

(4)、若是時間一致,那麼返回HTTP狀態碼304,儘管FirstFilter程序在攔截處理中向ServletResponse對象中寫入的數據也傳送給了瀏覽器。可是瀏覽器由於收到304則顯示原來緩存的內容;

(5)、若是時間不一致,就返回HTTP狀態碼200,瀏覽器接到以後,會丟棄舊文件,把新文件緩存起來,並顯示到瀏覽器中。

解決:

在test.html文件中增長一個空格又刪去這個空格,形成text.html文件被修改過的假象,而後在瀏覽器刷新test.html頁面的訪問。

相關文章
相關標籤/搜索