一、動態(tài)代理實現(xiàn)AOP的缺陷
在上一篇文章細說Spring——AOP詳解(動態(tài)代理實現(xiàn)AOP)中講解了如何使用動態(tài)代理實現(xiàn)AOP,雖然Java動態(tài)代理為我們提供了非常靈活的代理機制,但Java動態(tài)代理是基于接口的,如果目標對象沒有實現(xiàn)接口我們該如何代理呢?這時候我們就需要使用CGLIB來實現(xiàn)AOP了。
二、CGLIB實現(xiàn)代理的原理
我們先創(chuàng)建一個目標對象
package demo1;
/**
* Created by Yifan Jia on 2018/6/9.
*/
public class SomeService {
public String doFirst() {
System.out.println("執(zhí)行doFirst()方法");
return "abcde";
}
public void doSecond() {
System.out.println("doSecond()方法");
}
}
針對這個目標類,假如我們要使用動態(tài)代理實現(xiàn)AOP,那么我們只能在寫一個增強的接口,然后讓目標類實現(xiàn)增強接口,然后我們就可以使用動態(tài)代理實現(xiàn)目標類的增強,可是假如我們不想讓目標類實現(xiàn)其他的接口,那么我們就只能使用CGLIB技術來實現(xiàn)目標類的增強了。
CGLIB實現(xiàn)目標類增強的原理是這樣的:CGLIB會動態(tài)創(chuàng)建一個目標類的子類,然后返回該子類的對象,也就是增強對象,至于增強的邏輯則是在子類中完成的。我們知道子類要么和父類有一樣的功能,要么就比父類功能強大,所以CGLIB是通過創(chuàng)建目標類的子類對象來實現(xiàn)增強的,所以:
目標子類 = 目標類 + 增強邏輯
至于CGLIB底層是如何動態(tài)的生成一個目標類的子類,它是使用動態(tài)字節(jié)碼技術,我們知道我們編寫的Java對象都是先編譯為.class文件,然后由類加載器加載到內存中變?yōu)橐粋€Java對象的,動態(tài)字節(jié)碼技術就是通過轉換字節(jié)碼生成新的類來實現(xiàn)改變一個類的內部邏輯的。至于更基礎的部分,我也沒有深入的研究,有興趣的可以自己研究一下。
三、使用CGLIB實現(xiàn)AOP
這里需要注意,我們需要導入CGLIB的相關包才能使用CGLIB,這里需要導入兩個包:
-
cglib-nodep-3.2.0.jar:使用nodep包不需要關聯(lián)asm的jar包,jar包內部包含asm的類. -
cglib-3.2.0.jar:使用此jar包需要關聯(lián)asm的jar包,否則運行時報錯.
版本可以自行選擇,我是有的Maven導入的jar包,依賴如下:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.0</version>
</dependency>
通過CGLIB代理實現(xiàn)如下:
- 首先實現(xiàn)一個
MethodInterceptor,方法調用會被轉發(fā)到該類的intercept()方法。 - 然后在需要使用
SomeService(目標對象)的時候,通過CGLIB動態(tài)代理獲取代理對象。
我們仍然使用上面的SomeService作為目標對象,然后我們實現(xiàn)一個MethodInterceptor,在實現(xiàn)MethodInterceptor之前,我們先看一下這個接口是什么:
public interface MethodInterceptor extends Callback {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable;
}
這里解釋一下各個參數(shù)的意思:
- Object為由CGLib動態(tài)生成的代理類實例
- Method為上文中實體類所調用的被代理的方法引用
- Object[]為參數(shù)值列表
- MethodProxy為生成的代理類對方法的代理引用。
下面是實現(xiàn)的一個MethodInterceptor,同時寫了創(chuàng)建增強對象的邏輯即myCglibCreator()方法,該方法返回增強對象。
public class CglibFactory implements MethodInterceptor {
public SomeService myCglibCreator() {
Enhancer enhancer = new Enhancer();
//將目標類設置為父類,cglib動態(tài)代理增強的原理就是子類增強父類,cglib不能增強目標類為final的類
//因為final類不能有子類
enhancer.setSuperclass(SomeService.class);
//設置回調接口,這里的MethodInterceptor實現(xiàn)類回調接口,而我們又實現(xiàn)了MethodInterceptor,其實
//這里的回調接口就是本類對象,調用的方法其實就是intercept()方法
enhancer.setCallback(this);
//create()方法用于創(chuàng)建cglib動態(tài)代理對象
return (SomeService) enhancer.create();
}
//回調接口的方法
//回調接口的方法執(zhí)行的條件是:代理對象執(zhí)行目標方法時會調用回調接口的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
//這里實現(xiàn)將返回值字符串變?yōu)榇髮懙倪壿? if(result != null) {
result = ((String) result).toUpperCase();
}
return result;
}
}
其中,Object result = methodProxy.invokeSuper(o, objects);調用代理類實例上的methodProxy方法的父類方法(即實體類SomeService中對應的方法),然后返回目標方法的返回值result,然后實現(xiàn)增強的邏輯,將返回的字符串變?yōu)榇髮懙?。下面是測試代碼:
package demo1;
/**
* Created by Yifan Jia on 2018/6/9.
*/
public class Test {
public static void main(String[] args) {
SomeService target = new SomeService();
SomeService proxy = new CglibFactory(target).myCglibCreator();
String result = proxy.doFirst();
System.out.println(result);
proxy.doSecond();
}
}
結果截圖:
上述代碼中,我們通過CGLIB的Enhancer來指定要代理的目標對象、實際處理代理邏輯的對象,最終通過調用create()方法得到代理對象,對這個對象所有非final方法的調用都會轉發(fā)給MethodInterceptor.intercept()方法,在intercept()方法里我們可以加入任何邏輯,比如修改方法參數(shù),加入日志功能、安全檢查功能等;通過調用MethodProxy.invokeSuper()方法,我們將調用轉發(fā)給原始對象,具體到本例,就是SomeService的具體方法。CGLIG中MethodInterceptor的作用跟JDK動態(tài)代理代理中的InvocationHandler很類似,都是方法調用的中轉站。
四、總結
我們學習了通過CGLIB實現(xiàn)動態(tài)增強,但是CGLIB也有其缺陷,那就是必須目標類必須是可以繼承的,如果目標類不可繼承,那么我們就無法使用CGLIB來增強該類,現(xiàn)在我們已經學習完了Spring AOP中兩種AOP的實現(xiàn)機制,我們可以稱JDK動態(tài)代理實現(xiàn)的AOP為面向接口的動態(tài)增強,將CGLIB實現(xiàn)的AOP稱為面向子類的動態(tài)增強。