Filter與Servlet的區別和聯系


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負責攔截所有的用戶請求,並將請求的信息記錄在日志中。

 

復制代碼
代碼
public class LogFilter implements Filter 
{
//FilterConfig可用於訪問Filter的配置信息
private FilterConfig config;
//實現初始化方法
public void init(FilterConfig config)
{
this.config = config; 
}
//實現銷毀方法
public void destroy()
{
this.config = null; 
}
//執行過濾的核心方法
public void 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;   
  
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>   
復制代碼

 

 然后發布,發現打印的日志如下:

。。。

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() ..

 

 

下面是一個實例:

 

代碼

 

 

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

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

param-name:指定參數名。

param-value:指定參數值。

該Filter的配置片段如下:

代碼

 

上面配置片段中粗體字代碼為該Filter指定了3個配置參數,指定loginPage為/login.jsp,proLogin為/proLogin.jsp,這表明:如果沒有登錄該應用,普通用戶只能訪問/login.jsp和/proLogin.jsp頁面。只有當用戶登錄該應用后才可自由訪問其他頁面。

實際上Filter和Servlet極其相似,區別只是Filter不能直接對用戶生成響應。實際上Filter里doFilter()方法里的代碼就是從多個Servlet的service()方法里抽取的通用代碼,通過使用Filter可以實現更好的復用。

 

web.xml 中的listener、 filter、servlet 加載順序及其詳解

在項目中總會遇到一些關於加載的優先級問題,剛剛就遇到了一個問題,由於項目中使用了quartz任務調度,quartz在web.xml中是使用listener進行監聽的,使得在tomcat啟動的時候能馬上檢查數據庫查看那些任務未被按時執行,而數據庫的配置信息在是在web.xml中使用servlet配置的,導致tomcat啟動后在執行quartz任務時報空指針,原因就是servlet中的數據庫連接信息未被加載。網上查詢了下web.xml中配置的加載優先級:

        首先可以肯定的是,加載順序與它們在 web.xml 文件中的先后順序無關。即不會因為 filter 寫在 listener 的前面而會先加載 filter。最終得出的結論是:listener -> filter -> servlet

        同時還存在着這樣一種配置節:context-param,它用於向 ServletContext 提供鍵值對,即應用程序上下文信息。我們的 listener, filter 等在初始化時會用到這些上下文中的信息,那么 context-param 配置節是不是應該寫在 listener 配置節前呢?實際上 context-param 配置節可寫在任意位置,因此真正的加載順序為:context-param -> listener -> filter -> servlet

        對於某類配置節而言,與它們出現的順序是有關的。以 filter 為例,web.xml 中當然可以定義多個 filter,與 filter 相關的一個配置節是 filter-mapping,這里一定要注意,對於擁有相同 filter-name 的 filter 和 filter-mapping 配置節而言,filter-mapping 必須出現在 filter 之后,否則當解析到 filter-mapping 時,它所對應的 filter-name 還未定義。web 容器啟動時初始化每個 filter 時,是按照 filter 配置節出現的順序來初始化的,當請求資源匹配多個 filter-mapping 時,filter 攔截資源是按照 filter-mapping 配置節出現的順序來依次調用 doFilter() 方法的。

        servlet 同 filter 類似 ,此處不再贅述。

       由此,可以看出,web.xml 的加載順序是: context-param -> listener -> filter -> servlet ,而同個類型之間的實際程序調用的時候的順序是根據對應的 mapping 的順序進行調用的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM