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就能夠阻止某些非法的訪問請求。服務器
一、在一個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
一個Filter程序就是一個Java類,這個類必須實現Filter接口。javax.servlet.Filter接口中定義了三個方法:init、doFilter、destory。
(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對象,要使用它則必然出錯)。
當一個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鏈的對象
該方法在Web容器卸載Filter對象以前被調用,也僅執行一次。能夠完成與init方法相反的功能,釋放被該Filter對象打開的資源,例如:關閉數據庫鏈接和IO流。
該接口用於定義一個Filter鏈的對象應該對外提供的方法,這個接口只定義了一個doFilter方法。
public void doFilter(ServletRequest request, ServletResponse response) throws java.io.IOException.ServletException
FilterChain接口的doFilter方法用於通知Web容器把請求交給Filter鏈中的下一個Filter去處理,若是當前調用此方法的Filter對象是Filter鏈中的最後一個Filter,那麼將把請求交給目標Servlet程序去處理。
一、與普通的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集合對象。
一個<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-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對資源的多種調用方式都進行攔截。
一、
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頁面的訪問。