一、基本概念
之前我們用一篇博文介紹了Servlet相關(guān)的知識,有了那篇博文的知識積淀,今天我們學(xué)習(xí)Filter將會非常輕松,因為Filter有很多地方和Servlet類似,下面在講Filter的時候,就閑話不絮了。
Filter稱之為過濾器,是用來做一些攔截的任務(wù)。比如客戶端請求服務(wù)器的某個資源時(可以是Servlet、JSP、HTML等等),我們可以攔截。當(dāng)服務(wù)器返回資源給客戶端的時候,我們也可以攔截。這樣我們就可以在調(diào)用資源之前和之后分別加入一些業(yè)務(wù)邏輯。

當(dāng)我們對某個資源加上多個過濾器的時候,就形成了過濾鏈。請求(request)會依次通過鏈上的過濾器,響應(yīng)(response)會依次以相反的順序通過過濾器。

二、樣例分析
和使用servlet一樣,要使用filter只需要兩步:1.編寫自己的filter,實現(xiàn)javax.servlet.Filter。2.在web.xml中注冊該filter。
TestFilter.java
public class TestFilter implements Filter {
public TestFilter() {}
public void init(FilterConfig fConfig) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("before doFilter");
chain.doFilter(request, response);
System.out.println("after doFilter");
}
}
web.xml
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.nantang.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/test</url-pattern>
</filter-mapping>
假定我們在本機部署應(yīng)用,應(yīng)用上下文路徑是/demo,那么在瀏覽器里面輸入http://localhost:8080/demo/test。則在控制臺會輸出:"before doFilter"和"after doFilter"。這是我們的請求和響應(yīng)受到了攔截,在調(diào)用具體的Servlet或其他資源前后執(zhí)行了我們編寫的邏輯(例子中就是往控制臺打印信息)。
這里需要注意的是,filter可以對url進行過濾,也可以針對具體的servlet進行過濾,只需要制定servlet的名稱,如:
<filter-mapping>
<filter-name>TestFilter</filter-name>
<servlet-name>TestServlet</servlet-name>
</filter-mapping>
三、源碼分析
我們自己編寫的filter都需要實現(xiàn)javax.servlet.Filter接口,下面我們看一下Filter接口的繼承層級,并逐一分析。

1 FilterConfig
當(dāng)容器去初始化一個filter的時候,就會根據(jù)web.xml文件的配置和當(dāng)前的運行環(huán)境去構(gòu)造一個FilterCongif實例并傳給filter。所以一個filter對應(yīng)一個FilterConfig實例。

public interface FilterConfig {
public String getFilterName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration getInitParameterNames();
}
getFilterName方法返回我們在web.xml里面配置的名稱。
getServletContext返回ServletContext實例,這個和之前介紹Servlet的時候講到的是一個東西。一個應(yīng)用就一個ServletContext實例。
getInitParameter和getInitParameterNames和Servlet的類似。就是獲取在web.xml里面配置的初始化參數(shù),如:
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>com.nantang.filter.TestFilter</filter-class>
<init-param>
<param-name>param1</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</filter>
2 FilterChain
FilterChain就是我們上面說的過濾鏈,當(dāng)請求或響應(yīng)被filter攔截時,容器提供FilterChain實例給filter讓其使用。
public interface FilterChain {
public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
}
FilterChain里面就一個方法doFilter,這個方法就是調(diào)用過濾鏈的下一個filter,如果當(dāng)前filter是鏈中最后的一個,則跳轉(zhuǎn)至請求的資源或返回響應(yīng)給客戶端。
3 Filter
Filter里面就三個方法,也就是它的生命周期。和Servlet類似。
public interface Filter {
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
public void destroy();
}

3.1 init
filter的初始化方法,跟servlet一樣在其生命周期內(nèi)只會被執(zhí)行一次。但是filter初始化的時機和servlet不一樣,當(dāng)servlet容器(比如Tomcat)啟動完成后就會檢索web.xml里面配置的filter,從上往下依次回調(diào)每個filter的init方法對其進行初始化。(這里的filterConfig參數(shù)是容器構(gòu)造并傳入的。)
3.2 doFilter
實現(xiàn)攔截邏輯的地方,比如一個請求被攔截,這里可以在調(diào)用具體資源之前編寫一些業(yè)務(wù)邏輯。然后調(diào)用chain.doFilter流轉(zhuǎn)到后置filter,如果當(dāng)前filter是鏈中最后的一個,則跳轉(zhuǎn)至請求的資源。當(dāng)chain.doFilter執(zhí)行完成后,可以再寫一些業(yè)務(wù)邏輯。然后容器將執(zhí)行權(quán)流轉(zhuǎn)到前置filter,如果當(dāng)前filter是鏈中最前的一個,則將響應(yīng)返回給客戶端。(這里的參數(shù)chain是容器構(gòu)造并傳入的。)
3.3 destory
和servlet的destroy方法同樣的道理,當(dāng)servlet容器關(guān)閉或需要更多內(nèi)存的時候,會銷毀filter。這個方法就使得servlet容器擁有回收資源的能力。
同樣地,destroy方法在filter的生命周期中只會被調(diào)用一次。
四、總結(jié)
到這里filter就算結(jié)束了,我們可以發(fā)現(xiàn)filter的執(zhí)行模型和servlet很類似,都是servlet容器在調(diào)度。整個生命周期都是容器在管控。