文章目錄
- 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則是考慮如何把散落在應用中多處相同的功能剝離出來,使得這些剝離出來的邏輯與業務邏輯相分離的問題。
讓我們先來看一個生活中的案例:
每家每戶都有一個電表來監控用電量,這樣電力公司就知道應該收取多少費用了。雖然每一台用電裝置都可以自裝一個用電統計的硬體,但這樣是極不合理,成本上也不劃算的,因為每個用電裝置更多關注的是自身的功能是否完善的問題,吸塵器考慮的是清潔效果,微波爐考慮的是加熱效果等等。電力公司隻需要在合适的地方,比如用電線路入戶的地方,統一安裝一個電表,就能很好的解決監控所有用電裝置電量的問題。
軟體系統中的一些功能就像我們家裡的電表一樣。這些功能需要用到應用程式的多個地方,但是我們又不想在每個點都明确調用它們。
圖中展示了一個被劃分為子產品的典型應用。每個子產品的核心功能都是為特定業務領域提供服務,但是這些子產品都需要類似的輔助功能,例如安全和事務管理。
在軟體開發中,散布于應用中多處的功能,被稱為橫切關注點(cross-cutting concern)。通常來講,這些橫切關注點從概念上是與應用的業務邏輯相分離的(但往往會直接嵌入到應用的業務邏輯中)。把這些橫切關注點與業務邏輯相分離正是AOP所要解決的問題。
2. 代理模式簡介
2.1 代理模式的概念
AOP在實作上采用了設計模式中的動态代理模式,是以,在深入學習SpringAOP之前,我們先來一起了解和學習一下這種強大的設計模式。
代理模式的定義:為其他對象提供一種代理,以控制對這個對象的通路。換句通俗的話來說,它是一種使用代理對象來執行目标對象的方法,并在代理對象中增強目标對象方法的一種設計模式。
生活中最常見的代理模式就是”中介“。假如說我現在想買一輛二手車,雖然我可以自己去找車源,做品質檢測等一系列的車輛過戶流程,但是這确實太浪費我得時間和精力了。我隻是想買一輛車而已為什麼我還要額外做這麼多事呢?于是我就通過中介公司來買車,他們來給我找車源,幫我辦理車輛過戶流程,我隻是負責選擇自己喜歡的車,然後付錢就可以了。
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();
}