Spring Aop是使用動態代理技術動態的生成目標對象的代理對象完成對目標方法增強的,要徹底理解Spring Aop就需要先理解動態代理,動態代理分爲兩種jdk動態代理和cglib動態代理
Jdk動態代理:針對的是實現了接口的目標類,它使用jdk中的Proxy來創建代理對象。
關於動態代理和jdk動態代理,我在另一篇文章中有詳細描述,下面我們 來看一下Cglib動態代理
Cglib動態代理:針對沒有實現接口的目標類,它使用字節碼增強的技術,通過動態創建目標類的子類對象在子類對象中增強目標類的方法實現,它需要引入asm.jar依賴。
Cglib動態代理是如何實現的呢?下面通過一個例子加以說明:
創建一個UserDao類:
public class UserDao {
public void addUser(){
System.out.println("添加用戶");
}
public void deleteUser(){
System.out.println("刪除用戶");
}
}
創建代理類:
public class cglibProxy implements MethodInterceptor {
private UserDao target;//被代理的目標類
public void cglibProxy(UserDao target){
super(target);
this.target=target;
}
//創建代理對象
public Object createProxy(){
// 1.聲明Enhancer類實例,用於生產代理類
Enhancer enhancer = new Enhancer();
// 2.設置被代理類字節碼,CGLIB根據字節碼生成被代理類的子類
enhancer.setSuperclass(target.getClass());
// 3.//設置回調函數
enhancer.setCallback(this);
// 4.創建代理:
return (UserDao) enhancer.create();
}
/**
* 回調函數
* @param proxy 目標對象
* @param method 要增強的方法
* @param args 要增強的方法參數
* @param methodProxy 方法的代理對象 * */
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//過濾不需要該業務的方法
if("addUser".equals(method.getName())) {
//調用目標對象的方法(執行UserDao對象即被代理對象的addUser方法)
Object result = methodProxy.invokeSuper(proxy, args);
//記錄日誌
Record.addRecord();
return result;
}else if("deleteUser".equals(method.getName())){
//.....
return methodProxy.invokeSuper(proxy, args);
}
//如果不需要增強直接執行原方法
return methodProxy.invokeSuper(proxy, args);
}
}
調用:
public class MyTest {
public static void main(String[] args) {
//創建目標對象
UserDao user= new UserDao();
//創建代理類對象
cglibProxy cglibProxy = new cglibProxy (user);
//生成目標類的代理對象
UserDao proxy = (UserDao)cglibProxy.createProxy();
//調用addUser方法
proxy.addUser();
}
}
打印的結果是:添加用戶 記錄日誌,addUser方法被增強了
從上面的例子可以看到通過createProxy方法就可以創建代理對象,我們細緻分析一下它創建對象的過程:
public void createProxy(){
// 1.聲明Enhancer類實例,用於生產代理類
Enhancer enhancer = new Enhancer();
// 2.設置被代理類字節碼,CGLIB根據字節碼生成被代理類的子類
enhancer.setSuperclass(target.getClass());
// 3.//設置回調函數
enhancer.setCallback(this);
// 4.創建代理:
return (UserDao) enhancer.create();
}
第一行代碼: Enhancer enhancer = new Enhancer();中的Enhancer是一個非常重要的類,它允許爲非接口類型創建一個JAVA代理,Enhancer動態的創建給定類的子類並且攔截代理類的所有的方法,和JDK動態代理不一樣的是不管是接口還是類它都能正常工作。
第二行代碼:enhancer.setSuperclass(target.getClass());這行代碼的意思是設置被代理類字節碼,爲什麼要設置被代理類的字節碼呢?CGLIB底層使用了ASM(一個短小精悍的字節碼操作框架)來操作字節碼生成新的類。所以需要獲取字節碼,巧婦難爲無米之炊哈。
第三行代碼:enhancer.setCallback(this);設置回調函數,這裏有一個參數this表示當前對象也就是cglibUser對象,因爲cglibUser類實現了接口MethodInterceptor,也實現了接口的方法:intercept,這個方法就是回調方法。當我們去調用代理類對象的方法時就會轉變爲去執行這個方法。
第四行代碼:return (UserDao) enhancer.create();這行代碼就是去創建目標對象的代理對象,他是通過創建目標對象的子類對象來實現的。
再來詳細的分析一下回調方法:intercept
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//如果方法名爲addUser
if("addUser".equals(method.getName())) {
//調用目標對象的方法(執行UserDao對象即被代理對象的addUser方法)
Object result = methodProxy.invokeSuper(proxy, args);
//記錄日誌
Record.addRecord();
return result;
}else if("deleteUser".equals(method.getName())){
//.....
return methodProxy.invokeSuper(proxy, args);
}
//如果不需要增強直接執行原方法
return methodProxy.invokeSuper(proxy, args);
}
我們看看這個方法的參數:intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
第一個參數:Object proxy表示目標對象,也就是需要被代理的對象
第二個參數:Method method表示要被增強的方法
第三個參數:Object[] args 表示方法的參數
第四個參數:MethodProxy methodProxy每個被代理的方法都對應一個MethodProxy對象,它是由cglib生成的,methodProxy.invokeSuper方法最終調用目標類的原始方法,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升。
在這個方法裏面我們可以判斷當前執行的方法是否爲需要被增強的方法,並做相應的增強處理。
比較一下jdk動態代理和cglib動態代理:
一:jdk動態代理使用jdk中的類Proxy來創建代理對象,它使用反射技術來實現,我們通過它來實現Spring Aop方法增強的時候不需要導入其它依賴。cglib需要引入相關依賴:asm.jar,它使用字節碼增強技術來實現。
二:當目標類實現了接口的時候Spring Aop默認使用jdk動態代理方式來增強方法,沒有實現接口的時候使用cglib動態代理方式增強方法
三: 在jdk1.8前,cglib生成的代理對象的執行效率高於jdk方式,1.8之後基本沒有區別。