AOP程式設計--Filter使用,過濾器和攔截器的區別

weixin_34185560發表於2017-05-26

AOP(Aspect-Oriented Programming,面向切面程式設計)是一種程式設計思想,並不是一種具體的實現,談到實現一般有Filter和代理模式兩種常見的使用方式,spring中的AOP也是封裝代理模式完成的,可以說是OOP(Object-Oriented Programing,物件導向程式設計)的補充和完善。OOP利用封裝、繼承和多型把一切事物打造成物件結構,但是對於所有物件中都存在的一些公共行為,OOP就顯得無能為力,也就是說OOP允許你定義從上到下的關係,但並不適合定義從左到右的關係。抽象和介面雖好,但對所有不相干的物件建立共同的介面或父類未免有些生硬,例如日誌功能,日誌程式碼幾乎散佈在所有的物件層次中,而它和散佈到物件的核心功能毫無關係,對於其他型別的程式碼,如安全性、異常處理和透明的持續性也是如此。因此,為減少這種大量的重複程式碼,面向切面技術誕生了,AOP和OOP的關係好似JSP和Servlet的關係,以此之長,補彼之短。

Java中常見的AOP技術有兩個,分別是Filter和代理模式(也可以稱為過濾器和攔截器),Filter是基於回撥函式(請看《Java回撥機制解析》),代理模式是基於Java反射技術,代理模式又分為靜態代理和動態代理,動態代理就是攔截器的簡單實現。(過濾器和攔截器的區別可參見《過濾器和攔截器的區別》)他們各自實現的功能不同,原理如出一轍。如下圖,以新增使用者為例,採用Module1架構模式,從圖中可以看出無論程式從左向右或者從右向左執行都必須經過Filter,Filter在Request到達JSP(Servlet)前截獲Request並進行預處理,也可以在Response離開JSP(Servlet)時處理Response,然後對Request進行統一的設定後繼續向後傳遞,比如可以在Filter完成字符集的設定,使用者身份的識別,敏感詞彙的過濾等等,配置Filter個數不限。

下面就以設定字符集為例,首先建立一個Class檔案,並讓他實現Filter介面,覆寫init和doFilter,程式碼如下:

package com.snail.drp.util.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 CharsetEncodingFilter implements Filter {  
    private String endcoding;  
      
    @Override  
    public void destroy() {  
        // TODO Auto-generated method stub  
    }  
  
    @Override  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
        request.setCharacterEncoding(endcoding);  
          
        //繼續執行  
        chain.doFilter(request, response);    
    }  
  
    @Override  
    public void init(FilterConfig filterConfig) throws ServletException {  
        this.endcoding = filterConfig.getInitParameter("encoding");  
    }  
}

Filter雖不是一個Servlet,但它需要在web.xml檔案中配置之後才能使用,具體解釋看程式碼中的註釋,如下:

<filter>        
        <filter-name>CharsetEncodingFilter</filter-name>  
        <filter-class>com.snail.drp.util.filter.CharsetEncodingFilter</filter-class>  
        <!--在Filter初始化時,設定編碼格式  -->  
        <init-param>  
            <param-name>encoding</param-name>  
            <param-value>GB18030</param-value>  
        </init-param>  
    </filter>  
    <!-- 設定Filter範圍,指對Post請求起作用 -->  
    <filter-mapping>  
        <filter-name>CharsetEncodingFilter</filter-name>  
        <url-pattern>*.jsp</url-pattern>  
    </filter-mapping>  
    <filter-mapping>  
        <filter-name>CharsetEncodingFilter</filter-name>  
        <url-pattern>/servlet/*</url-pattern>  
    </filter-mapping></span>  

Filter是職責鏈模式的經典應用,從上面示例程式碼可以看出,實現了Filter介面的doFilter方法主要用於截獲Request物件,把截獲的請求處理後(設定字符集),呼叫FilterChain 介面的chain.doFilter(request,response)方法,把處理權傳遞給下一個filter。

其中,FilterChain維護了一個連結串列,連結串列中存放著配置物件的鏈條,每次使用者呼叫 一次chain.doFilter(request, response),連結串列就去取下一個配置物件,再通過配置物件 得到下一個filter,然後呼叫該filter,接著在filter裡寫的邏輯就被執行了。

相關文章