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

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

二、樣例分析
和使用servlet一樣,要使用filter只需要兩步:1.編寫自己的filter,實(shí)現(xiàn)javax.servlet.Filter。2.在web.xml中注冊(cè)該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>
假定我們?cè)诒緳C(jī)部署應(yīng)用,應(yīng)用上下文路徑是/demo,那么在瀏覽器里面輸入http://localhost:8080/demo/test。則在控制臺(tái)會(huì)輸出:"before doFilter"和"after doFilter"。這是我們的請(qǐng)求和響應(yīng)受到了攔截,在調(diào)用具體的Servlet或其他資源前后執(zhí)行了我們編寫的邏輯(例子中就是往控制臺(tái)打印信息)。
這里需要注意的是,filter可以對(duì)url進(jìn)行過(guò)濾,也可以針對(duì)具體的servlet進(jìn)行過(guò)濾,只需要制定servlet的名稱,如:
<filter-mapping>
<filter-name>TestFilter</filter-name>
<servlet-name>TestServlet</servlet-name>
</filter-mapping>
三、源碼分析
我們自己編寫的filter都需要實(shí)現(xiàn)javax.servlet.Filter接口,下面我們看一下Filter接口的繼承層級(jí),并逐一分析。

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

public interface FilterConfig {
public String getFilterName();
public ServletContext getServletContext();
public String getInitParameter(String name);
public Enumeration getInitParameterNames();
}
getFilterName方法返回我們?cè)趙eb.xml里面配置的名稱。
getServletContext返回ServletContext實(shí)例,這個(gè)和之前介紹Servlet的時(shí)候講到的是一個(gè)東西。一個(gè)應(yīng)用就一個(gè)ServletContext實(shí)例。
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就是我們上面說(shuō)的過(guò)濾鏈,當(dāng)請(qǐng)求或響應(yīng)被filter攔截時(shí),容器提供FilterChain實(shí)例給filter讓其使用。
public interface FilterChain {
public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
}
FilterChain里面就一個(gè)方法doFilter,這個(gè)方法就是調(diào)用過(guò)濾鏈的下一個(gè)filter,如果當(dāng)前filter是鏈中最后的一個(gè),則跳轉(zhuǎn)至請(qǐng)求的資源或返回響應(yīng)給客戶端。
3 Filter
Filter里面就三個(gè)方法,也就是它的生命周期。和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)只會(huì)被執(zhí)行一次。但是filter初始化的時(shí)機(jī)和servlet不一樣,當(dāng)servlet容器(比如Tomcat)啟動(dòng)完成后就會(huì)檢索web.xml里面配置的filter,從上往下依次回調(diào)每個(gè)filter的init方法對(duì)其進(jìn)行初始化。(這里的filterConfig參數(shù)是容器構(gòu)造并傳入的。)
3.2 doFilter
實(shí)現(xiàn)攔截邏輯的地方,比如一個(gè)請(qǐng)求被攔截,這里可以在調(diào)用具體資源之前編寫一些業(yè)務(wù)邏輯。然后調(diào)用chain.doFilter流轉(zhuǎn)到后置filter,如果當(dāng)前filter是鏈中最后的一個(gè),則跳轉(zhuǎn)至請(qǐng)求的資源。當(dāng)chain.doFilter執(zhí)行完成后,可以再寫一些業(yè)務(wù)邏輯。然后容器將執(zhí)行權(quán)流轉(zhuǎn)到前置filter,如果當(dāng)前filter是鏈中最前的一個(gè),則將響應(yīng)返回給客戶端。(這里的參數(shù)chain是容器構(gòu)造并傳入的。)
3.3 destory
和servlet的destroy方法同樣的道理,當(dāng)servlet容器關(guān)閉或需要更多內(nèi)存的時(shí)候,會(huì)銷毀filter。這個(gè)方法就使得servlet容器擁有回收資源的能力。
同樣地,destroy方法在filter的生命周期中只會(huì)被調(diào)用一次。
四、總結(jié)
到這里filter就算結(jié)束了,我們可以發(fā)現(xiàn)filter的執(zhí)行模型和servlet很類似,都是servlet容器在調(diào)度。整個(gè)生命周期都是容器在管控。