首頁 > 軟體

Spring AOP底層原理及代理模式

2022-05-30 18:01:50

Spring AOP底層原理代理模式

一、什麼是 AOP

AOP 就是面向切面程式設計,是 OOP(物件導向程式設計)的延續。

利用 AOP 可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式可用性,同時也提高了開發效率。

通俗一點說,不用修改原始碼,可以給原始碼增加新的功能。

二、AOP 底層原理

AOP 底層原理是使用動態代理。

那代理是什麼?有動態代理,那是不是還有靜態代理?

1. 什麼是代理?

就是為一個目標物件提供一個代理物件,並由代理物件控制對目標物件的參照。使用代理物件,是為了在不修改目標物件的基礎上,增強目標物件的業務邏輯。

比如目標物件 A,代理物件是 B。

  • 那麼現在 B 對 A 進行參照,可以實現 A 有的功能。
  • 另外,B 還可以在自身進行一些新功能,最終不需要修改目標物件 A 。

而代理分為靜態代理和動態代理,區別是:

靜態代理有真實的代理類存在,就是我們會程式碼中建立一個代理類,並在代理類的方法中呼叫目標物件的方法,以此來完成代理的工作。動態代理的代理類沒有在程式碼中建立一個代理類,而是在執行時在JVM裡面建立代理物件。

2. 什麼是靜態代理

靜態代理是有實實在在的代理類存在,並且和目標類實現相同的介面。

比如,有一個轉賬業務,現在希望給它增加功能,使在轉賬之前確認轉賬人身份,以及轉賬之後通知收款人。

(1) 介面 AccountServiceDao :

package com.pingguo.spring5.dao;
public interface AccountServiceDao {
    // 主業務邏輯,轉賬
    void transfer();
}

(2) 介面 AccountServiceDao 的實現類:

package com.pingguo.spring5.dao;
public class AccountServiceImpl implements AccountServiceDao {
    @Override
    public void transfer() {
        System.out.println("呼叫dao層,完成轉賬主業務.");
    }
}

(3) 代理類 AccountProxy :

package com.pingguo.spring5.proxy;
import com.pingguo.spring5.dao.AccountServiceDao;
public class AccountProxy implements AccountServiceDao {
    // 目標物件
    private AccountServiceDao target;
    public AccountProxy(AccountServiceDao target) {
        this.target = target;
    }
    /**
     *  代理方法,實現對目標方法的增強
     */
    @Override
    public void transfer() {
        before();
        target.transfer();
        after();
    }
    /**
     *  增強的功能,轉賬之前使用
     */
    private void before() {
        System.out.println("對轉賬人身份進行驗證.");
    }
    /**
     *  增強的功能,轉賬之後使用
     */
    private void after() {
        System.out.println("轉賬完成,已通知收款人.");
    }
}

在代理類中:

  • 新增了新增了目標物件,並且有參構造方法裡需要傳入目標物件。
  • 代理方法裡,呼叫了目標物件裡的轉賬方法 target.transfer()。
  • before() 和 after() 則是 2個增強的方法,分別作用於 target.transfer() 的前面和後面。

(4) 執行測試新建一個測試方法,執行看下結果:

@Test
    public void testProxy() {
        // 建立目標物件
        AccountServiceDao target = new AccountServiceImpl();
        // 建立代理物件
        AccountProxy proxy = new AccountProxy(target);
        proxy.transfer();
    }
  • 這裡先建立了目標物件
  • 再建立代理物件,並且把目標物件傳入
  • 最後呼叫代理物件裡的,被增強過的方法 transfer()。

結果:

對轉賬人身份進行驗證.
呼叫dao層,完成轉賬主業務.
轉賬完成,已通知收款人.
Process finished with exit code 0

優點:

  • 效率高,因為所有的類都是已經編寫完成的,使用的時候只需要取得代理物件並且執行即可。
  • 同時也可以實現對目標物件中指定的方法進行增強。

缺點:

  • 與目標類實現相同的介面程式碼,冗餘。
  • 如果介面發生改變,代理類中的方法也要修改。
  • 代理類服務於一種型別的物件,如果要服務多型別的物件,那麼要為每種型別的物件都生成代理類。

3. 什麼是動態代理

與靜態代理的寫死方式相比,動態代理支援執行時動態生成代理物件這種方式。換句話說,動態代理並不存在代理類,代理物件直接由代理生成工具動態生成。

優點:

  • 用很少的程式碼對一個類的所有方法實現一樣的增強效果。
  • 在編碼時,代理邏輯與業務邏輯互相獨立,各不影響,減少侵入,降低耦合。

缺點:

相對於靜態代理,它不能增強其中的某一個方法。

對於動態代理,針對於是否存在介面的情況下,又分為 2 種:

  • 有介面的情況下,使用 JDK 動態代理。
  • 無介面的情況下,使用 CGLIB 動態代理。

使用 JDK 動態代理

使用 JDK 動態代理,建立的是介面實現類的代理物件,以此來實現功能增強。

現在不需要上面建立過的實際代理類了 。

介面,為了後面的一些知識點的說明,裡面加個引數,轉賬的金額:

package com.pingguo.spring5.dao;
public interface AccountServiceDao {
    // 主業務邏輯,轉賬
    void transfer(int amount);
}

實現類:

package com.pingguo.spring5.dao;
public class AccountServiceImpl implements AccountServiceDao {
    @Override
    public void transfer(int amount) {
        System.out.println("呼叫dao層,完成轉賬主業務.金額:" + amount);
    }
}

在測試方法裡,直接使用動態代理:

@Test
    public void testDynamicProxy() {
        // 建立目標物件
        AccountServiceDao target = new AccountServiceImpl();
        // 建立代理物件
        AccountServiceDao proxy = (AccountServiceDao) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 目標類使用的類載入器
                target.getClass().getInterfaces(),  // 目標類實現的介面
                new InvocationHandler() {  // 呼叫處理器
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("對轉賬人身份進行驗證.");
                        Object res =  method.invoke(target, args);
                        System.out.println("轉賬完成,已通知收款人.");
                        return res;
                    }
                }
        );
        // 讓代理工作
        proxy.transfer(10000);
    }

執行結果:

對轉賬人身份進行驗證.
呼叫dao層,完成轉賬主業務.金額:10000
轉賬完成,已通知收款人.
Process finished with exit code 0

動態代理的過程:

  • 建立處理器 InvocationHandler範例。
  • 在呼叫目標物件時,會呼叫代理物件。
  • 代理物件去請求目標物件。invoke 方法就是呼叫目標物件的方法生成代理物件的過程。
  • 同時,在 invoke 方法中進行功能增強。

對於 invoke 中的 3 個引數,分別是:

  • Object proxy:代理物件,一般不會使用。
  • Method method:外面的代理物件呼叫的方法參照,這裡參照的就是 transfer()
  • Object[] args:外面的代理物件呼叫的方法裡面的引數,這裡就是引數 amount。

使用 CGLIB 動態代理

CGLIB動態代理的原理是生成目標類的子類,這個子類物件就是代理物件,代理物件是被增強過的。

注意,不管有沒有介面都可以使用 CGLIB 動態代理, 而不是隻有在無介面的情況下才能使用。

範例就暫時不放了,因為我本地環境問題,有個報錯始終未解決,後續再說,不影響繼續學習 spring。

以上就是Spring AOP底層原理及代理模式的詳細內容,更多關於Spring AOP底層原理的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com