Filter介紹
Filter可認(rèn)為是Servlet的一種“變種”,它主要用于對用戶請求進(jìn)行預(yù)處理,也可以對HttpServletResponse進(jìn)行后處理,是個典型的處理鏈。它與Servlet的區(qū)別在于:它不能直接向用戶生成響應(yīng)。完整的流程是:Filter對用戶請求進(jìn)行預(yù)處理,接著將請求交給Servlet進(jìn)行處理并生成響應(yīng),最后Filter再對服務(wù)器響應(yīng)進(jìn)行后處理。
Filter有如下幾個用處。
- 在HttpServletRequest到達(dá)Servlet之前,攔截客戶的HttpServletRequest。
- 根據(jù)需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和數(shù)據(jù)。
- 在HttpServletResponse到達(dá)客戶端之前,攔截HttpServletResponse。
- 根據(jù)需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和數(shù)據(jù)。
Filter有如下幾個種類。
- 用戶授權(quán)的Filter:Filter負(fù)責(zé)檢查用戶請求,根據(jù)請求過濾用戶非法請求。
- 日志Filter:詳細(xì)記錄某些特殊的用戶請求。
- 負(fù)責(zé)解碼的Filter:包括對非標(biāo)準(zhǔn)編碼的請求解碼。
- 能改變XML內(nèi)容的XSLT Filter等。
- Filter可負(fù)責(zé)攔截多個請求或響應(yīng);一個請求或響應(yīng)也可被多個請求攔截。
創(chuàng)建一個Filter只需兩個步驟:
- 建Filter處理類;
- web.xml文件中配置Filter。
下面先介紹一個簡單的記錄日志的Filter,這個Filter負(fù)責(zé)攔截所有的用戶請求,并將請求的信息記錄在日志中。
代碼
public class LogFilter implements Filter
{
//FilterConfig可用于訪問Filter的配置信息
private FilterConfig config;
//實現(xiàn)初始化方法
public void init(FilterConfig config)
{
this.config = config;
}
//實現(xiàn)銷毀方法
public void destroy()
{
this.config = null;
}
//執(zhí)行過濾的核心方法
public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException
{
//---------下面代碼用于對用戶請求執(zhí)行預(yù)處理---------
//獲取ServletContext對象,用于記錄日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("開始過濾...");
//將請求轉(zhuǎn)換成HttpServletRequest請求
HttpServletRequest hrequest = (HttpServletRequest)request;
//記錄日志
context.log("Filter已經(jīng)截獲到用戶的請求地址: " + hrequest.getServletPath());
//Filter只是鏈?zhǔn)教幚?,請求依然放行到目的地?/span>
chain.doFilter(request, response);
//---------下面代碼用于對服務(wù)器響應(yīng)執(zhí)行后處理---------
long after = System.currentTimeMillis();
//記錄日志
context.log("過濾結(jié)束");
//再次記錄日志
context.log("請求被定位到" + hrequest.getRequestURI() + "所花的時間為: " + (after - before));
}
}
上面程序?qū)崿F(xiàn)了doFilter()方法,實現(xiàn)該方法就可實現(xiàn)對用戶請求進(jìn)行預(yù)處理,也可實現(xiàn)對服務(wù)器響應(yīng)進(jìn)行后處理——它們的分界線為是否調(diào)用了chain.doFilter(),執(zhí)行該方法之前,即對用戶請求進(jìn)行預(yù)處理;執(zhí)行該方法之后,即對服務(wù)器響應(yīng)進(jìn)行后處理。
在上面的請求Filter中,僅在日志中記錄請求的URL,對所有的請求都執(zhí)行chain.doFilter (request,reponse)方法,當(dāng)Filter對請求過濾后,依然將請求發(fā)送到目的地址。如果需要檢查權(quán)限,可以在Filter中根據(jù)用戶請求的HttpSession,判斷用戶權(quán)限是否足夠。如果權(quán)限不夠,直接調(diào)用重定向即可,無須調(diào)用chain.doFilter(request,reponse)方法。
==================
FirstFilter.java
==================
package com.test.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class FirstFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("before invoke firstFilter's chain.doFilter() ..");
chain.doFilter(request, response);
System.out.println("after invoke firstFilter's chain.doFilter() ..");
}
@Override
public void init(FilterConfig arg0) throws ServletException {
System.out.println("firstFilter init()...");
}
}
============
SecondFilter.java
=============
package com.test.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class SecondFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("before invoke secondFilter's chain.doFilter() ..");
chain.doFilter(request, response);
System.out.println("after invoke secondFilter's chain.doFilter() ..");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("secondFilter init()...");
}
}
==========
FirstServlet.java
==========
package com.test.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("servlet doGet be invoked...");
req.getRequestDispatcher("test.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(req, resp);
}
}
web.xml
代碼
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>firstFilter</filter-name>
<filter-class>com.test.filter.FirstFilter</filter-class>
</filter>
<filter>
<filter-name>secondFilter</filter-name>
<filter-class>com.test.filter.SecondFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>secondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>firstServlet</servlet-name>
<servlet-class>com.alimama.servlet.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>firstServlet</servlet-name>
<url-pattern>/firstServlet</url-pattern>
</servlet-mapping>
</web-app>
然后發(fā)布,發(fā)現(xiàn)打印的日志如下:
。。。
firstFilter init()...
secondFilter init()...
。。。
信息: Server startup in 3665 ms
這里過濾器初始化好了。
當(dāng)我們訪問我們的 應(yīng)用:http://127.0.0.1:8080/appName
發(fā)現(xiàn)打印日記如下:
before invoke secondFilter's chain.doFilter() ..
before invoke firstFilter's chain.doFilter() ..
after invoke firstFilter's chain.doFilter() ..
after invoke secondFilter's chain.doFilter() ..
當(dāng)我們將web.xml中filter的位置進(jìn)行調(diào)整后(注意filter-mapping的順序):
代碼
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>firstFilter</filter-name>
<filter-class>com.test.filter.FirstFilter</filter-class>
</filter>
<filter>
<filter-name>secondFilter</filter-name>
<filter-class>com.test.filter.SecondFilter</filter-class>
</filter>
<SPAN style="COLOR: #ff0000"> <filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>secondFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping></SPAN>
<servlet>
<servlet-name>firstServlet</servlet-name>
<servlet-class>com.alimama.servlet.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>firstServlet</servlet-name>
<url-pattern>/firstServlet</url-pattern>
</servlet-mapping>
</web-app>
然后在啟動應(yīng)用,會看到打?。?/p>
before invoke firstFilter's chain.doFilter() ..
before invoke secondFilter's chain.doFilter() ..
after invoke secondFilter's chain.doFilter() ..
after invoke firstFilter's chain.doFilter() ..
下面是一個實例:
上面Filter的doFilter方法里3行斜體字代碼用于獲取Filter的配置參數(shù),而程序中粗體字代碼則是此Filter的核心,①號代碼按配置參數(shù)設(shè)置了request編碼所用的字符集,接下來的粗體字代碼判斷session范圍內(nèi)是否有user屬性——沒有該屬性即認(rèn)為沒有登錄,如果既沒有登錄,而且請求地址也不是登錄頁和處理登錄頁,系統(tǒng)直接跳轉(zhuǎn)到登錄頁面。
在web.xml文件中配置該Filter,使用init-param元素為該Filter配置參數(shù),init-param可接受如下兩個子元素:
param-name:指定參數(shù)名。
param-value:指定參數(shù)值。
該Filter的配置片段如下:
上面配置片段中粗體字代碼為該Filter指定了3個配置參數(shù),指定loginPage為/login.jsp,proLogin為/proLogin.jsp,這表明:如果沒有登錄該應(yīng)用,普通用戶只能訪問/login.jsp和/proLogin.jsp頁面。只有當(dāng)用戶登錄該應(yīng)用后才可自由訪問其他頁面。
實際上Filter和Servlet極其相似,區(qū)別只是Filter不能直接對用戶生成響應(yīng)。實際上Filter里doFilter()方法里的代碼就是從多個Servlet的service()方法里抽取的通用代碼,通過使用Filter可以實現(xiàn)更好的復(fù)用。