圖文詳解 Spring AOP

語言: CN / TW / HK

簡介: 圖文詳解 Spring AOP,看完必懂。。

學習一個模塊的設計主要是看接口設計,通過接口設計我們就能夠從整體知道模塊怎麼實現的,具體實現就是組裝這些接口來進行實現的,知道了模塊接口設計,實現也就變得很簡單了。

本文主要從aop背景出發點,來自己去想需要哪些接口,就能夠描述一個模塊的功能設計規則。

AOP產生背景

使用 面向對象 編程 ( OOP )有一些弊端,當需要為多個不具有繼承關係的對象引人同一個公共行為時,例如日誌、安全檢測等,我們只有在每個對象裏引用公共行為,這樣程序中就產生了大量的重複代碼,程序就不便於維護了,所以就有了一個 對面向對象編程的補充,即面向方面編程 ( AOP ), AOP 所關注的方向是橫向的,區別於 OOP 的縱向。

什麼是AOP

什麼是面向方面編程,3個過程:

  • 找到橫切點:首要目標確定在程序的哪個位置進行橫切邏輯
  • 橫切邏輯(業務代碼):橫切邏輯代碼,這個就是橫切業務代碼,與aop無關
  • 織入:將橫切邏輯織入到橫切點

開發者主要關心的是橫切邏輯的編寫,只需要很少的代碼編寫確定橫切點有哪些,而不需要去為每個橫切點添加橫切邏輯,不然就是面向對象編程了。

既然是橫向的編程,那麼在我們的程序中,哪些可以作為橫線切入點呢?

看下示例代碼:

public class Test {
    public static void main(String[] args) {
        //@1
       B b = new B();
       //@2
       b.method();
       //@3
        B.say();
    }
    static class B {
        //字段
        //@4
        private String name;
        //構造方法
        public B() {
            //@1.1
            }
        //對象方法
        public void method(){
            //@2.2
        }
        //靜態方法
        static void say(){
            //@3.3
        }
    }
}

所以我們可以將橫切點主要分為兩大類:字段、方法。方法又分為很多種,

橫切點有很多地方,從代碼上看得見的,有如下幾個地方:

  • 使用構造函數創建對象
    • 構造函數執行
  • 對象方法調用
    • 對象方法執行
  • 靜態方法調用
    • 靜態方法執行
  • 反射讀寫對象字段

目標1:找到橫切點

那麼怎麼去定義一個橫切點呢?怎麼用一個接口來描述一個橫切點呢?

在Java中,一切皆對象,在Java中一個類有2方面內容:字段、方法(構造函數、對象方法、靜態方法),java中使用AccessibleObject來抽象公共行為。方法:就是一段可以執行的程序,一段代碼。

所以在橫切點接口中,首先一個功能就是返回給用户當前橫切點,有兩種情況:

  • 如果橫切點作用於對象(對象字段、對象方法、構造函數),則不僅需要返回AccessibleObject,還需要返回當前對象,因為調用通過反射調用對象方法需要傳入當前對象。
  • 如果橫切點作用於類,則僅返回AccessibleObject即可。

另一個接口功能就是要不要考慮在橫切點來控制多個橫切邏輯的調用。這個可以有框架支持,也可以由橫切點控制。這對應的就是責任鏈模式的API設計。比如tomcat中的Filter鏈式調用就是以集合形式調用;netty中的Handler組織就是以鏈表形式。如果是以集合形式調用,則在橫切點接口需要定義一個方法來鏈式調用。(aop聯盟的JoinPoint採用是集合形式調用)

那麼AOP聯盟使用JointPoint接口來定義橫切點。

public interface Joinpoint {
 Object proceed() throws Throwable;
 Object getThis();
 AccessibleObject getStaticPart();
}

Object proceed() throws Throwable : 鏈式調用橫切點

Object getThis(); 返回連接點當前對象。如果當前連接點是靜態,比如靜態方法,則該方法返回null,因為反射不需要對象,而且靜態方法是通過類調用的,壓根就沒有對象 ,所以返回null。spring aop不支持靜態方法的攔截,所以在spring中這裏返回的就是目標對象(被代理對象)

AccessibleObject getStaticPart(); 返回連接點靜態部分,對於連接點是方法,返回的就是Method對象。

現在對連接點的設計比較清晰了,然後就是對連接點的擴展了,比如可執行程序(構造方法、Method)的子接口,字段的子接口(aop聯盟沒有定義,只有方法級別的)。

AOP聯盟對連接點接口的設計:

比如在MethodInvocation,就是返回Method。

目標2:橫切邏輯(增強)抽象定義

增強的抽象,其實就需要連接點信息,畢竟增強是要投入到一個地方的,所以需要連接點信息。

在aop聯盟的接口定義:

Advice作為一個tag標識,在aop聯盟中使用攔截器來作為增強的命名,這裏完全可以去掉Interceptor,而直接定義一個MethodAdvice。之所以定義為Interceptor,是因為攔截器命名更符合編程命名規範,讓人從命名就知道接口功能。

在MethodInterceptor,傳入連接點信息(因為是方法攔截,所以這裏是方法級別的連接點接口定義)

Object invoke(MethodInvocation invocation) throws Throwable;

目標3:織入

首先就是怎麼織入。織入由兩種方案。

  • 靜態織入:採用自定義類加載器機制。自定義類加載器根據織入規則在加載class文件期間對class文件動手織入橫切邏輯,然後將改動後的class文件交給JVM運行。
  • 動態織入:由多種選擇,動態代理(JDK Proxy)、動態字節碼生成技術(cglib)

spring採用動態織入。動態織入就是生成代理對象,代理對象中維護了當前連接點所有攔截器,然後調用目標方法時被代理類攔截,在代理類中作aop功能。

來一個完整的流程圖:

Spring AOP的實現基於AOP聯盟接口標準設計實現的,全局看下aopalliance有哪些接口以及接口的API設計,我們上面已經分析完了。

AOP聯盟的接口很少:

版權聲明: 本文內容由阿里雲實名註冊用户自發貢獻,版權歸原作者所有,阿里雲開發者社區不擁有其著作權,亦不承擔相應法律責任。具體規則請查看《阿里雲開發者社區用户服務協議》和《阿里雲開發者社區知識產權保護指引》。如果您發現本社區中有涉嫌抄襲的內容,填寫侵權投訴表單進行舉報,一經查實,本社區將立刻刪除涉嫌侵權內容。