Table of Contents
1. AOP介紹
AOP稱爲面向切面編程,它是一種編程思想,是對OOP的補充,可以進一步提高編程效率,在程序開發中主要用來解決一些系統層面上的問題,比如日誌,事務,權限等待,Struts2的攔截器設計就是基於AOP的思想,是個比較經典的例子。
AOP的基本概念:
(1)Aspect(切面):通常是一個類,裏面可以定義切入點和通知
(2)JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用
(3)Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現爲書寫切入點表達式
(5)Target(目標對象):就是被增強的目標類,也稱之爲委託類。
(6)AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基於接口,後者基於子類。
2.AOP的實現
AOP前面說過是編程思想,所以需要具體的實現方法來實現。
AOP的實現主要分爲靜態代理和動態代理,靜態代理的實現方式是AspectJ;而動態代理則以Spring AOP爲代表。
- AspectJ(靜態)
- AspectJ是語言級的實現,它擴展了Java語言,定義了AOP語法。
- AspectJ在編譯期織入代碼,它有一個專門的編譯器,用來生成遵守Java字節碼規範的class文件。
- Spring AOP(動態)
- Spring AOP使用純Java實現,它不需要專門的編譯過程,也不需要特殊的類裝載器。
- Spring AOP在運行時通過代理的方式織入代碼,只支持方法類型的連接點。
- Spring支持對AspectJ的集成。
3.Spring AOP的兩種實現方式
- JDK動態代理(依賴接口)
- Java提供的動態代理技術,可以在運行時創建接口的代理實例。
- Spring AOP默認採用此種方式,在接口的代理實例中織入代碼。
- CGLib動態代理(不依賴接口)
- 採用底層的字節碼技術,在運行時創建子類代理實例。
- 當目標對象不存在接口時,Spring AOP會採用此種方式,在子類實例中織入代碼。
4.AspectJ實現示例
AspectJ是靜態代理的增強,所謂的靜態代理就是AOP框架會在編譯階段生成AOP代理類,因此也稱爲編譯時增強。
靜態代理模式:靜態代理說白了就是在程序運行前就已經存在代理類的字節碼文件,代理類和原始類的關係在運行前就已經確定。廢話不多說,我們看一下代碼,爲了方便閱讀,博主把單獨的class文件合併到接口中,讀者可以直接複製代碼運行:
package test.staticProxy;
// 接口
public interface IUserDao {
void save();
void find();
}
//目標對象
class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("模擬:保存用戶!");
}
@Override
public void find() {
System.out.println("模擬:查詢用戶");
}
}
/**
靜態代理
特點:
1. 目標對象必須要實現接口
2. 代理對象,要實現與目標對象一樣的接口
*/
class UserDaoProxy implements IUserDao{
// 代理對象,需要維護一個目標對象
private IUserDao target = new UserDao();
@Override
public void save() {
System.out.println("代理操作: 開啓事務...");
target.save(); // 執行目標對象的方法
System.out.println("代理操作:提交事務...");
}
@Override
public void find() {
target.find();
}
}
測試結果:
靜態代理雖然保證了業務類只需關注邏輯本身,代理對象的一個接口只服務於一種類型的對象,如果要代理的方法很多,勢必要爲每一種方法都進行代理。再者,如果增加一個方法,除了實現類需要實現這個方法外,所有的代理類也要實現此方法。增加了代碼的維護成本。那麼要如何解決呢?答案是使用動態代理。
5.JDK動態代理、CGLib動態代理 實現示例
5.1 JDK動態代理
JDK動態代理通過反射來接收被代理的類,並且要求被代理的類必須實現一個接口。JDK動態代理的核心是InvocationHandler
接口和Proxy
類。
5.2 CGLIB動態代理
如果目標類沒有實現接口,那麼Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態的生成某個類的子類。注意:CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記爲final
,那麼它是無法使用CGLIB做動態代理的。
5.3 JDK Proxy VS Cglib
JDK Proxy 的優勢:
-
最小化依賴關係,減少依賴意味着簡化開發和維護,JDK 本身的支持,更加可靠;
-
平滑進行 JDK 版本升級,而字節碼類庫通常需要進行更新以保證在新版上能夠使用;
Cglib 框架的優勢:
-
可調用普通類,不需要實現接口;
-
高性能;
6.小結
AspectJ在編譯時就增強了目標對象,Spring AOP的動態代理則是在每次運行時動態的增強,生成AOP代理對象。區別在於生成AOP代理對象的時機不同,相對來說AspectJ的靜態代理方式具有更好的性能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理。