天天看點

Spring AOP之動态代理AOP之動态代理

文章目錄

  • AOP之動态代理
    • 1. 什麼是AOP
    • 2. 代理模式簡介
      • 2.1 代理模式的概念
      • 2.2 代理模式的好處
      • 2.3 代理模式的種類
    • 3. 靜态代理模式
    • 4. 動态代理模式
      • 4.1 JDK Proxy
      • 4.2 CGLIB

AOP之動态代理

1. 什麼是AOP

我們知道Spring一直緻力于簡化我們的Java開發,并且用到了依賴注入(Dependency Injection)與AOP(Aspect-Oriented Programming)這兩項非常重要的技術:

  • DI主要解決了在類和類之間有依賴關系的時候,如何通過注入的方式(屬性注入、構造器注入)形成松耦合
  • 而今天要學習的AOP則是考慮如何把散落在應用中多處相同的功能剝離出來,使得這些剝離出來的邏輯與業務邏輯相分離的問題。

讓我們先來看一個生活中的案例:

每家每戶都有一個電表來監控用電量,這樣電力公司就知道應該收取多少費用了。雖然每一台用電裝置都可以自裝一個用電統計的硬體,但這樣是極不合理,成本上也不劃算的,因為每個用電裝置更多關注的是自身的功能是否完善的問題,吸塵器考慮的是清潔效果,微波爐考慮的是加熱效果等等。電力公司隻需要在合适的地方,比如用電線路入戶的地方,統一安裝一個電表,就能很好的解決監控所有用電裝置電量的問題。

軟體系統中的一些功能就像我們家裡的電表一樣。這些功能需要用到應用程式的多個地方,但是我們又不想在每個點都明确調用它們。

Spring AOP之動态代理AOP之動态代理

圖中展示了一個被劃分為子產品的典型應用。每個子產品的核心功能都是為特定業務領域提供服務,但是這些子產品都需要類似的輔助功能,例如安全和事務管理。

在軟體開發中,散布于應用中多處的功能,被稱為橫切關注點(cross-cutting concern)。通常來講,這些橫切關注點從概念上是與應用的業務邏輯相分離的(但往往會直接嵌入到應用的業務邏輯中)。把這些橫切關注點與業務邏輯相分離正是AOP所要解決的問題。

2. 代理模式簡介

2.1 代理模式的概念

AOP在實作上采用了設計模式中的動态代理模式,是以,在深入學習SpringAOP之前,我們先來一起了解和學習一下這種強大的設計模式。

代理模式的定義:為其他對象提供一種代理,以控制對這個對象的通路。換句通俗的話來說,它是一種使用代理對象來執行目标對象的方法,并在代理對象中增強目标對象方法的一種設計模式。

生活中最常見的代理模式就是”中介“。假如說我現在想買一輛二手車,雖然我可以自己去找車源,做品質檢測等一系列的車輛過戶流程,但是這确實太浪費我得時間和精力了。我隻是想買一輛車而已為什麼我還要額外做這麼多事呢?于是我就通過中介公司來買車,他們來給我找車源,幫我辦理車輛過戶流程,我隻是負責選擇自己喜歡的車,然後付錢就可以了。

Spring AOP之動态代理AOP之動态代理

2.2 代理模式的好處

從上圖我們可以看出,在程式設計中使用代理模式的一些好處:

  • 中介隔離:在調用方(

    Caller

    )不能或不想直接與目标對象(

    Target

    )打交道的時候,代理對象(

    Proxy

    )可以起到兩者之間中介的作用。
  • 開閉原則:我們可以通過給代理對象增加新的功能來擴充目标對象的功能,這樣我們隻需要修改代理類,而不需要修改目标類,符合代碼設計的OCP原則(

    Open Closed Principle

    ,對擴充是開放的,對修改是關閉的)。

2.3 代理模式的種類

根據代理對象建立的不同,分為兩種代理模式:

  • 靜态代理:由程式員或者特定工具生成源代碼來産生代理對象。在程式運作前,代理類的位元組碼檔案(.class)就已經存在了
  • 動态代理:在程式運作期間,運用反射機制、位元組碼生成技術來産生代理類和執行個體。

3. 靜态代理模式

要實作靜态代理,我們首先使用接口的方式來封裝被代理的行為:

public interface BuyCar {

	void buy();

}
           

分别讓目标和代理都來實作這個接口,這樣,對于調用方來說,無論和代理還是目标打交道,執行的代碼都是一緻的,都可以執行購買行為。

public class Customer implements BuyCar {

	@Override
	public void buy() {
		System.out.println("客戶選車、付款");
	}

}
           

為目标Customer編寫對應的代理類,在其中增加新的服務功能:

public class CarProxy implements BuyCar {

	private Customer customer;

	public CarProxy(Customer customer) {
		this.customer = customer;
	}

	@Override
	public void buy() {
		System.out.println("售前服務:尋找車源、品質檢測");
		// 讓使用者執行真正的購買行為
		customer.buy();
		System.out.println("售後服務:過戶服務、售後咨詢");
	}

}
           

從上面可以看到,我們最終讓客戶執行了購買行為,除此之外,為了讓客戶享受更為完善的服務,我們還擴充了尋找車源、品質檢測、售後咨詢等其它服務。在一個複雜的應用中,我們當然不是僅僅列印幾行字,我們可以封裝單獨的方法來做這些事情,甚至還可以調用其它的類來執行這些輔助邏輯。

測試代碼:

public static void main(String[] args) {
    BuyCar caller = new CarProxy(new Customer());
    caller.buy();
}
           

觀察上面這個寫法,雖然對于

caller

來說,等式右邊出現了一個客戶(

target

),看上去好像

caller

還是和

target

有關聯,但是不要忘了,我們可以借助Spring的DI技術來讓這個客戶注入給二手車代理(

proxy

),同時也讓這個二手車代理”消失“在等式的右邊。

4. 動态代理模式

靜态代理模式最大的缺陷就是,我們需要為每一個被代理的目标類都編寫一個代理類。而動态代理可以很好的解決這個問題。JDK的Proxy和開源架構CGLIB可以分别在不同的情況幫助我們生成代理。

4.1 JDK Proxy

當目标的被代理方法抽取了接口時,可以使用JDK Proxy。

首先需要編寫一個調用處理器

public class CarHandler implements InvocationHandler {

	private Object target;

	public CarHandler(Object target) {
		this.target = target;
	}

	// proxy 代理
	// method 被代理的方法
	// args 被代理方法執行所需的參數
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("售前服務");
        // 方法委派
		Object result = method.invoke(target, args);
		System.out.println("售後服務");
		return result;
	}

}
           

invoke

方法會在代理對象的被代理方法調用的時候觸發。

測試代碼:

public static void main(String[] args) {
    Customer customer = new Customer();
    Class<?> clazz = Customer.class;
    // 參數1:目标類的類加載器
    // 參數2:目标類所實作的接口數組
    // 參數3:調用處理器
    BuyCar caller = (BuyCar) Proxy.newProxyInstance(
                        clazz.getClassLoader(), 
                        clazz.getInterfaces(),
                        new CarHandler(customer));
    caller.buy();
}
           

根據測試代碼可以看出,

Proxy

将會根據類加載器的定義,生成一個實作了目标接口的代理對象來為調用方提供代理服務。在這個代理對象中,包含了我們想要擴充的其它服務邏輯,這些邏輯是通過我們編寫的

CarHandler

單獨封裝出來的。

4.2 CGLIB

當目标類沒有實作接口時,我們可以通過開源的CGLIB來實作。

引入Cglibku

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.8</version>
</dependency>
           

編寫Handler

public class CglibHandler implements MethodInterceptor {

    // obj 目标對象
    // method intercept方法本身
    // args 方法調用時所需的參數
    // proxy 被代理的方法對象
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("售前服務");
        // 方法委派
		Object result = proxy.invokeSuper(obj, args);
		System.out.println("售後服務");
		return result;
	}

}
           

測試代碼

public static void main(String[] args) {
    // 建立代理生成工具
    Enhancer enhancer = new Enhancer();
    // 設定目标為代理的父類型
    enhancer.setSuperclass(Customer.class);
    // 設定回調處理器
    enhancer.setCallback(new CglibHandler());
    // 建立代理對象
    Customer proxy = (Customer) enhancer.create();
    proxy.buy();
}
           

目标為代理的父類型

enhancer.setSuperclass(Customer.class);

// 設定回調處理器

enhancer.setCallback(new CglibHandler());

// 建立代理對象

Customer proxy = (Customer) enhancer.create();

proxy.buy();

}