過濾器、攔截器和AOP的分析與對比


一、過濾器(Filter)

1.1 簡介

過濾器攔截的是URL

Java的過濾器能夠為我們提供系統級別的過濾,也就是說,能過濾所有的web請求,這一點,是攔截器無法做到的。

Spring中自定義過濾器(Filter)一般只有一個方法,返回值是void,當請求到達web容器時,會探測當前請求地址是否配置有過濾器,有則調用該過濾器的方法(可能會有多個過濾器),然后才調用真實的業務邏輯,至此過濾器任務完成。過濾器並沒有定義業務邏輯執行前、后等,僅僅是請求到達就執行。

請求執行流程如下圖:

1.2 應用場景

  • 自動登錄
  • 統一設置編碼格式
  • 訪問權限控制
  • 敏感字符過濾等

1.3 源碼分析

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();
}

過濾器方法的入參有request,response,FilterChain,其中FilterChain是過濾器鏈,使用比較簡單,而request,response則關聯到請求流程,因此可以對請求參數做過濾和修改,同時FilterChain過濾鏈執行完,並且完成業務流程后,會返回到過濾器,此時也可以對請求的返回數據做處理(如果Filter的urlPatterns設置為“/*”的話,doFilter()方法會執行兩次,向服務器發起請求執行一次,服務器返回結果再執行一次)。

urlPatterns
配置要攔截的資源

  1. 以指定資源匹配。例如"/index.jsp"
  2. 以目錄匹配。例如"/servlet/*"
  3. 以后綴名匹配。例如"*.jsp"
  4. 通配符,攔截所有web資源。"/*"

二、攔截器(Interceptor)

2.1 簡介

攔截器只能攔截部分web請求(URL)。(攔截器是基於反射機制實現的,攔截的對象只能是實現了接口的類,而不能攔截url這種連接)

Java里的攔截器提供的是非系統級別的攔截,也就是說,就覆蓋面來說,攔截器不如過濾器強大,但是更有針對性。Java中的攔截器是基於Java反射機制實現的,更准確的划分,是基於JDK實現的動態代理。

單個攔截器執行流程:

  1. 程序先執行preHandle()方法,如果該方法的返回值為true,則程序會繼續向下執行處理器中的方法,否則將不再向下執行。
  2. 在業務處理器(即控制器Controller類)處理完請求后,會執行postHandle()方法,然后會通過DispatcherServlet向客戶端返回響應。
  3. 在DispatcherServlet處理完請求后,才會執行afterCompletion()方法。

多個攔截器執行流程:

  1. 當有多個攔截器同時工作時,它們的preHandle()方法會按照配置文件中攔截器的配置順序執行,而它們的postHandle()方法和afterCompletion()方法則會按照配置順序的反序執行。

2.2 應用場景

  • 日志記錄:記錄請求信息的日志
  • 權限檢查:如登錄檢查
  • 性能檢測:檢測方法的執行時間

2.2 源碼分析

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
  1. preHandle() 方法:該方法會在控制器方法前執行,其返回值表示是否中斷后續操作。當其返回值為true時,表示繼續向下執行;當其返回值為false時,會中斷后續的所有操作(包括調用下一個攔截器和控制器類中的方法執行等)。
  2. postHandle()方法:該方法會在控制器方法調用之后,且解析視圖之前執行。可以通過此方法對請求域中的模型和視圖做出進一步的修改。
  3. afterCompletion()方法:該方法會在整個請求完成,即視圖渲染結束之后執行。可以通過此方法實現一些資源清理、記錄日志信息等工作。

在Spring框架中,我們可以直接繼承HandlerInterceptorAdapter.java這個抽象類,來實現我們自己的攔截器。

三、面向切面編程(AOP)

3.1 簡介

面向切面攔截的是類的元數據(包、類、方法名、參數等)

相對於攔截器更加細致,而且非常靈活,攔截器只能針對URL做攔截,而AOP針對具體的代碼,能夠實現更加復雜的業務邏輯。

AOP是對OOP的一種補充和完善,將代碼中重復的工作抽取出來成為一個切面,減少代碼的耦合性,例如事務、日志。

3.2 應用場景

  • 事務控制
  • 異常處理
  • 打印日志等

3.3 AOP術語

  1. 方面(Aspect):一個關注點的模塊化,這個關注點實現可能另外橫切多個對象。事務管理是J2EE應用中一個很好的橫切關注點例子。方面用Spring的Advisor或攔截器實現。
  2. 連接點(Joinpoint):程序執行過程中明確的點,如方法的調用或特定的異常被拋出。
  3. 通知(Advice):在特定的連接點,AOP框架執行的動作。各種類型的通知包括“around”、“before”和“throws”通知。通知類型將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護一個“圍繞”連接點的攔截器鏈。
  4. 切入點(Pointcut):指定一個通知將被引發的一系列連接點的集合。AOP框架必須允許開發者指定切入點,例如,使用正則表達式。
  5. 引入(Introduction):添加方法或字段到被通知的類。Spring允許引入新的接口到任何被通知的對象。例如,你可以使用一個引入使任何對象實現IsModified接口,來簡化緩存。
  6. 目標對象(Target Object):包含連接點的對象,也被稱作被通知或被代理對象。
  7. AOP代理(AOP Proxy):AOP框架創建的對象,包含通知。在Spring中,AOP代理可以是JDK動態代理或CGLIB代理。
  8. 編織(Weaving):組裝方面來創建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。

各種通知類型包括:

  1. Around通知:包圍一個連接點的通知,如方法調用。這是最強大的通知。Aroud通知在方法調用前后完成自定義的行為,它們負責選擇繼續執行連接點或通過返回它們自己的返回值或拋出異常來短路執行。
  2. Before通知:在一個連接點之前執行的通知,但這個通知不能阻止連接點前的執行(除非它拋出一個異常)。
  3. AfterThrowing通知:在方法拋出異常時執行的通知。Spring提供強制類型的Throws通知,因此你可以書寫代碼捕獲感興趣的異常(和它的子類),不需要從Throwable或Exception強制類型轉換。
  4. AfterReturning通知:在連接點正常完成后執行的通知,例如,一個方法正常返回,沒有拋出異常。

如同AspectJ,Spring提供所有類型的通知,推薦使用最為合適的通知類型來實現需要的行為。例如,如果只是需要用一個方法的返回值來更新緩存,最好實現一個AfterReturning通知,而不是Around通知,雖然Around通知也能完成同樣的事情。使用最合適的通知類型使編程模型變得簡單,代碼可讀性更高,並能減少潛在錯誤。

四、三者對比

三者功能類似,但各有優勢,從過濾器--》攔截器--》AOP,攔截規則越來越細致,執行順序依次是過濾器、攔截器、切面。一般情況下數據被過濾的時機越早對服務的性能影響越小,因此我們在編寫相對比較公用的代碼時,優先考慮過濾器,然后是攔截器,最后是aop。比如權限校驗,一般情況下,所有的請求都需要做登陸校驗,此時就應該使用過濾器在最頂層做校驗;日志記錄,一般日志只會針對部分邏輯做日志記錄,而且牽扯到業務邏輯完成前后的日志記錄,因此使用過濾器不能細致地划分模塊,此時應該考慮攔截器,然而攔截器也是依據URL做規則匹配,因此相對來說不夠細致,因此我們會考慮到使用AOP實現,AOP可以針對代碼的方法級別做攔截,很適合日志功能。

Spring的攔截器與Servlet的Filter有相似之處,比如二者都是AOP編程思想的體現,都能實現權限檢查、日志記錄等。不同的是:

  1. 使用范圍不同:Filter是Servlet規范規定的,只能用於Web程序中。而攔截器既可以用於Web程序,也可以用於Application、Swing程序中。
  2. 規范不同:Filter是在Servlet規范中定義的,是Servlet容器支持的。而攔截器是在Spring容器內的,是Spring框架支持的。
  3. 使用的資源不同:同其他的代碼塊一樣,攔截器也是一個Spring的組件,歸Spring管理,配置在Spring文件中,因此能使用Spring里的任何資源、對象,例如Service對象、數據源、事務管理等,通過IOC注入到攔截器即可;而Filter則不能。
  4. 深度不同:
    • Filter在只在Servlet前后起作用。實際上Filter和Servlet極其相似,區別只是Filter不能直接對用戶生成響應。實際上Filter里doFilter()方法里的代碼就是從多個Servlet的service()方法里抽取的通用代碼,通過使用Filter可以實現更好的復用。Filter是一個可以復用的代碼片段,可以用來轉換Http請求、響應和頭信息。Filter不像Servlet,它不能產生一個請求或者響應,它只是修改對某一資源的請求,或者修改從某一資源的響應。
    • 而攔截器能夠深入到方法前后、異常拋出前后等,因此攔截器的使用具有更大的彈性。所以在Spring構架的程序中,要優先使用攔截器。
    • AOP相對於攔截器更加細致,而且非常靈活,攔截器只能針對URL做攔截,而AOP針對具體的代碼,能夠實現更加復雜的業務邏輯。

五、參考文獻


免責聲明!

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



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