1. CGLib動態代理分析
JDK實現動態代理需要實現類通過接口定義業務方法,對於沒有接口的類,如何實現動態代理呢,這就需要CGLib了。CGLib采用了非常底層的字節碼技術,其原理是通過字節碼技術為一個類創建子類,並在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。
這里有兩個比較重要的角色:
- 被代理類:真實的業務處理類
- 方法攔截器:用來攔截被代理類
2. 使用代碼
dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
2.1 被代理類
public class UserDao {
public void save() {
System.out.println("業務處理");
}
}
2.2 方法攔截類
public class ProxyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object enhancerObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("在調用之前,我要干點啥呢?");
Object returnValue = methodProxy.invokeSuper(enhancerObj, args);
System.out.println("在調用之后,我要干點啥呢?");
return returnValue;
}
}
2.3 啟動類
public class Main {
public static void main(String[] args) {
//這里會保存生成之后的動態代理類
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\home");
//目標對象
Enhancer en = new Enhancer();
en.setSuperclass(UserDao.class);
en.setCallback(new ProxyMethodInterceptor());
UserDao userDao = (UserDao) en.create();
userDao.save();
}
}
可以看到,最終生成的類是
2.4 聊聊代理方法
public Object intercept(Object enhancerObj,//這個是最終的增強類
Method method,//這個是調用的方法
Object[] args,//這個是調用的參數
MethodProxy methodProxy) throws Throwable {
}
基於intercept方法的參數,調用被代理方法的方式主要有兩種
-
直接全部使用方法上的參數
methodProxy.invokeSuper(enhancerObj, args);
-
外部傳入被代理的類
public class ProxyFactory implements MethodInterceptor { //這里維護一個目標類,在調用攔截方法時使用目標類執行 private Object target; public ProxyFactory(Object target) { this.target = target; } @Override public Object intercept(Object enhancerObj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開啟事務"); // 執行目標對象的方法 Object returnValue = method.invoke(target, args); System.out.println("關閉事務"); return returnValue; } }
3. CGLib生成的代理類
JDK動態代理只能對實現了接口的類生成代理,而不能針對類 。CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。
因為是繼承,所以該類或方法最好不要聲明成final ,final可以阻止繼承和多態。
3.1 為什么是三個類? 三個類分別什么作用?
Jdk動態代理的攔截對象是通過反射的機制來調用被攔截實例方法的,反射的效率比較低,所以cglib采用了FastClass的機制來實現對被攔截方法的調用。FastClass機制就是對一個類的方法建立索引,調用方法時根據方法的簽名來計算索引,通過索引來直接調用相應的方法。
CGLIB生成的動態代理會包含3個class文件,一個是生成的代理類,另外兩個類都是FastClass機制需要的。一個class為生成的代理類中的每個方法建立了索引,另外一個則為我們被代理類的所有方法包含其父類的方法建立了索引。
3.2 主要看看生成的代理類
public class UserDao$$EnhancerByCGLIB$$8a1a4f11 extends UserDao implements Factory {
//代理類的防范
public final void save() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$save$0$Method, CGLIB$emptyArgs, CGLIB$save$0$Proxy);
} else {
super.save();
}
}
}
想深入的可以自己深入調試並看看最終三成的三個類