圖文詳解 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聯盟的介面很少:

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