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