1. 程式人生 > >Filter 執行順序介紹

Filter 執行順序介紹

Filter介紹 

Filter可認為是Servlet的一種“變種”,它主要用於對使用者請求進行預處理,也可以對HttpServletResponse進行後處理,是個典型的處理鏈。它與Servlet的區別在於:它不能直接向用戶生成響應。完整的流程是:Filter對使用者請求進行預處理,接著將請求交給Servlet進行處理並生成響應,最後Filter再對伺服器響應進行後處理。

Filter有如下幾個用處。

  • 在HttpServletRequest到達Servlet之前,攔截客戶的HttpServletRequest。
  • 根據需要檢查HttpServletRequest,也可以修改HttpServletRequest頭和資料。
  • 在HttpServletResponse到達客戶端之前,攔截HttpServletResponse。
  • 根據需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和資料。

Filter有如下幾個種類。

  • 使用者授權的Filter:Filter負責檢查使用者請求,根據請求過濾使用者非法請求。
  • 日誌Filter:詳細記錄某些特殊的使用者請求。
  • 負責解碼的Filter:包括對非標準編碼的請求解碼。
  • 能改變XML內容的XSLT Filter等。
  • Filter可負責攔截多個請求或響應;一個請求或響應也可被多個請求攔截。

建立一個Filter只需兩個步驟:

  • 建Filter處理類;
  • web.xml檔案中配置Filter。

下面先介紹一個簡單的記錄日誌的Filter,這個Filter負責攔截所有的使用者請求,並將請求的資訊記錄在日誌中。

複製程式碼 程式碼 publicclass LogFilter implements Filter 
{
//FilterConfig可用於訪問Filter的配置資訊private FilterConfig config;
//實現初始化方法publicvoid init(FilterConfig config)
{
this.config = config; 
}
//實現銷燬方法publicvoid destroy()
{
this.config =null
}
//執行過濾的核心方法publicvoid doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException
{
//---------下面程式碼用於對使用者請求執行預處理---------
//獲取ServletContext物件,用於記錄日誌ServletContext context =this.config.getServletContext(); 
long before = System.currentTimeMillis();
System.out.println(
"開始過濾...");
//將請求轉換成HttpServletRequest請求HttpServletRequest hrequest = (HttpServletRequest)request;
//記錄日誌context.log("Filter已經截獲到使用者的請求地址: "+ hrequest.getServletPath());
//Filter只是鏈式處理,請求依然放行到目的地址chain.doFilter(request, response); 
//---------下面程式碼用於對伺服器響應執行後處理---------long after = System.currentTimeMillis();
//記錄日誌context.log("過濾結束");
//再次記錄日誌context.log("請求被定位到"+ hrequest.getRequestURI() +"所花的時間為: "+ (after - before)); 
}
}
複製程式碼

上面程式實現了doFilter()方法,實現該方法就可實現對使用者請求進行預處理,也可實現對伺服器響應進行後處理——它們的分界線為是否呼叫了chain.doFilter(),執行該方法之前,即對使用者請求進行預處理;執行該方法之後,即對伺服器響應進行後處理。

在上面的請求Filter中,僅在日誌中記錄請求的URL,對所有的請求都執行chain.doFilter (request,reponse)方法,當Filter對請求過濾後,依然將請求傳送到目的地址。如果需要檢查許可權,可以在Filter中根據使用者請求的HttpSession,判斷使用者許可權是否足夠。如果許可權不夠,直接呼叫重定向即可,無須呼叫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;   
  
publicclass FirstFilter implements Filter {   
  
    @Override  
    
publicvoid destroy() {   
  
    }   
  
    @Override  
    
publicvoid 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  
    
publicvoid 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;   
  
publicclass SecondFilter implements Filter {   
  
    @Override  
    
publicvoid destroy() {   
  
    }   
  
    @Override  
    
publicvoid 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  
    
publicvoid 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;   
  
publicclass FirstServlet extends HttpServlet {   
  
    @Override  
    
protectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)   
            
throws ServletException, IOException {   
        System.out.println(
"servlet doGet be invoked...");   
        req.getRequestDispatcher(
"test.jsp").forward(req, resp);   
    }   
  
    @Override  
    
protectedvoid 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> 複製程式碼

 然後釋出,發現列印的日誌如下:

。。。

firstFilter init()...
secondFilter init()...

。。。
資訊: Server startup in 3665 ms

這裡過濾器初始化好了。

當我們訪問我們的 應用:http://127.0.0.1:8080/appName

發現列印日記如下:

before invoke secondFilter's chain.doFilter() ..
before invoke firstFilter's chain.doFilter() ..
after invoke firstFilter's chain.doFilter() ..
after invoke secondFilter's chain.doFilter() ..

當我們將web.xml中filter的位置進行調整後(注意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> 複製程式碼

然後在啟動應用,會看到列印:

before invoke firstFilter's chain.doFilter() ..

before invoke secondFilter's chain.doFilter() ..
after invoke secondFilter's chain.doFilter() ..

after invoke firstFilter's chain.doFilter() ..

下面是一個例項:

程式碼 複製程式碼 publicclass AuthorityFilter implements Filter 
{
//FilterConfig可用於訪問Filter的配置資訊private FilterConfig config;
//實現初始化方法publicvoid init(FilterConfig config)
{
  
this.config = config;
}
//實現銷燬方法publicvoid destroy()
{
this.config =null;
}
//執行過濾的核心方法publicvoid doFilter(ServletRequest request,ServletResponse response, FilterChain chain)throws IOException,ServletException{
//獲取該Filter的配置引數String encoding = config.getInitParameter("encoding");
String loginPage 
= config.getInitParameter("loginPage");
String proLogin 
= config.getInitParameter("proLogin");
//設定request編碼用的字符集request.setCharacterEncoding(encoding); //HttpServletRequest requ = (HttpServletRequest)request;
HttpSession session 
= requ.getSession(true);
//獲取客戶請求的頁面String requestPath = requ.getServletPath();
//如果session範圍的user為null,即表明沒有登入
//且使用者請求的既不是登入頁面,也不是處理登入的頁面if( session.getAttribute("user"==null&&!requestPath.endsWith(loginPage)&&!requestPath.endsWith(proLogin)){
//forward到登入頁面request.setAttribute("tip" , "您還沒有登入");
request.getRequestDispatcher(loginPage).forward(request, response);
}
//“放行”請求else{ chain.doFilter(request, response); }
}
}
複製程式碼

上面Filter的doFilter方法裡3行斜體字程式碼用於獲取Filter的配置引數,而程式中粗體字程式碼則是此Filter的核心,①號程式碼按配置引數設定了request編碼所用的字符集,接下來的粗體字程式碼判斷session範圍內是否有user屬性——沒有該屬性即認為沒有登入,如果既沒有登入,而且請求地址也不是登入頁和處理登入頁,系統直接跳轉到登入頁面。

在web.xml檔案中配置該Filter,使用init-param元素為該Filter配置引數,init-param可接受如下兩個子元素:

param-name:指定引數名。

param-value:指定引數值。

該Filter的配置片段如下:

程式碼 複製程式碼 <!-- 定義Filter --><filter><!-- Filter的名字 --><filter-name>authority</filter-name><!-- Filter的實現類 --><filter-class>lee.AuthorityFilter</filter-class><!-- 下面3個init-param元素配置了3個引數 --><init-param><param-name>encoding</param-name><param-value>GBK</param-value></init-param><init-param><param-name>loginPage</param-name><param-value>/login.jsp</param-value></init-param><init-param><param-name>proLogin</param-name><param-value>/proLogin.jsp</param-value></init-param></filter><!-- 定義Filter攔截的URL地址 --><filter-mapping><!-- Filter的名字 --><filter-name>authority</filter-name><!-- Filter負責攔截的URL --><url-pattern>/*</url-pattern></filter-mapping>