簡介
cglib是另外一種動態代理的方法,他和jdk動態代理的實現是有區別的,我們在之前見過jdk動態代理類是必須實現了介面的,而cglib不需要實現介面,但是必須保證類不含有final關鍵字,否則是無法代理的。 本文是從個人不小心遇到的cglib的死迴圈問題從而展開的分析。
cglib案例
下面我們來展示一個cglib的死迴圈案例。首先是要被代理的類,還是和常規的一樣,宣告自己的方法就行,但是要確保類和方法沒有被final關鍵字修飾。用final關鍵字修飾類會直接報異常,但是修飾方法不會拋異常,但是此方法不會被代理,但是不影響其他方法被代理。
public class InfoDemo {
public void welcome (String person){
System.out.println("welcome :" + person);
}
}
複製程式碼
下面是具體的代理類實現
public class CglibInfoProxy implements MethodInterceptor{
private Object target;
public Object newInstance(Object source){
target = source;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method!!!");
Object value = methodProxy.invoke(o, objects);
//Object value = methodProxy.invoke(this.target, objects);
//Object value = methodProxy.invokeSuper(o, objects);
return value;
}
public static void main(String[] args) {
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
instance.welcome("zhangsan");
}
}
複製程式碼
和我們的jdk動態代理看起來十分相似,只是兩個類實現的介面不同,並且生成物件的方法也不同。這裡非常坑的是invoke
方法和invokeSuper
的區別,如果是用invoke方法一定要使用被代理的物件也就是上文中的target,而如果呼叫invokeSuper方法,則一定要使用被代理後的o物件。
上述這個例子就會引發死迴圈,導致StackOverflowFlow,嘿嘿,學沒有,棧溢位的場景
具體為什麼會這樣,可以先思考一下,後面我們在原始碼實現中再去講解。現在我們先看一下執行結果
...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
at java.nio.CharBuffer.<init>(CharBuffer.java:281)
at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.write(PrintStream.java:526)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
at com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7.welcome(<generated>)
at com.eumji.proxy.cglib.InfoDemo$$FastClassByCGLIB$$2e560a7d.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...
複製程式碼
此處只展示部分的效果,具體可以自己試一下。
假如我們換成其餘他兩條語句將會是正確的輸出,具體結果如下
before method!!!
welcome :zhangsan
複製程式碼
原理解析
要想弄清楚的這到底是怎麼回事,首先我們要看一下cglib代理後的類是怎樣的,要想生成代理類的檔案,我們只需要在我們的main方法中取消掉這句方法的註釋
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");
就會在D盤的classes檔案下生成對應的代理class檔案,需要注意的是生成的代理class是有三個的,我們首先介紹一下我們最關心的InfoDemo代理類,其他的稍後合適的時機在描述其他兩個。
InfoDemo反編譯程式碼
其實就是將class檔案直接拖到IDEA中
public class InfoDemo$$EnhancerByCGLIB$$870a84d7 extends InfoDemo implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$welcome$0$Method;
private static final MethodProxy CGLIB$welcome$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
}
final void CGLIB$welcome$0(String var1) {
super.welcome(var1);
}
public final void welcome(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
} else {
super.welcome(var1);
}
}
...
}
複製程式碼
看程式碼就可以看出來cglib還是很複雜的,現在我們暫且可以看一下我們關心的welcome方法,從上面的程式碼中可以看到cglib是會為被代理類的方法同時生成兩個代理方法的,一個是同名的welcome方法
和CGLIB$welcome$0方法
1.CGLIB$welcome$0方法
直接呼叫被代理的方法,也就是啥都沒幹。
2.welcome方法
首先判斷有沒有設定callback,很明顯我們在程式碼中有設定即為CglibInfoProxy,所以就會呼叫CglibInfoProxy.intercept方法。
本來想分析一波生成代理類的過程,看了一下有點複雜,暫時就不分析了。。。。
invokeSuper方法
前面也提及了invoke和invokeSuper方法稍不注意就會出問題的問題,在這裡我們從程式碼的層面去追蹤一下,產生問題的原因。
我們看一下代理類方法invokeSuper的執行流程
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init(); //初始化fastInfo
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
複製程式碼
invokeSuper在這裡主要的作用就是初始化fastClassInfo。
init方法
private void init() {
if (this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
複製程式碼
上面的方法主要是載入methodProxy.FastClassInfo。ci是之前就初始化好的,其中c1指的就是被代理的類InfoDemo,c2則是com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$efe38465
這個代理類。
然後生成對應的f1和f2以及方法的下標i1和i2,i1和i2對應的就是在最前面所說的welcome方法
和CGLIB$welcome$0方法
,後面程式碼可以看出。
而f1則是對應InfoDemo$$FastClassByCGLIB$$2e560a7d
代理類,f2則對應InfoDemo$$EnhancerByCGLIB$$efe38465$$FastClassByCGLIB$$38345933
代理類。這些都可以在生成的代理class中去檢視。
當然這裡並沒有說到底什麼生成的,有興趣的可以自己看一下位元組碼是怎麼生成的。個人沒太看懂。
invoke和invokeSuper區別
為什麼要生成兩個代理類f1和f2,我相信你看過之前的方法應該注意到了,在上面我們提及到了invoke方法和invokeSuper方法,我們來對比一下invoke方法和invokeSuper方法的區別
public Object invoke(Object obj, Object[] args) throws Throwable {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
}
複製程式碼
我們可以看到invoke使用的是f1.invoke方法,而invokeSuper則是使用f2.invoke方法。
首先看一下f1對應的invoke方法邏輯
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
InfoDemo var10000 = (InfoDemo)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.welcome((String)var3[0]);
return null;
....
}
複製程式碼
直接呼叫InfoDemo物件的welcome方法。
所以這也就能解釋為什麼我們之前會發生迴圈呼叫invoke的方法了,因為我們傳入的var2是InfoDemo的代理物件,看最前面的代理類程式碼就可以看出,又會回到invoke方法,造成死迴圈。
再看一下f2中對應invoke的實現
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
efe38465 var10000 = (efe38465)var2;
int var10001 = var1;
try {
switch(var10001) {
....
var10000.CGLIB$finalize$1();
return null;
case 16:
var10000.CGLIB$welcome$0((String)var3[0]);
return null;
....
}
複製程式碼
因為我們此時我們傳入的var2是InfoDemo代理物件,所以最終會呼叫代理類中的CGLIB$welcome$0
方法。
小結
只是一次失敗的原始碼分析嘗試,不過弄清楚了造成呼叫死迴圈的原因,只能說cglib比jdk的動態代理複雜很多,主要體現在生成程式碼的邏輯和生成的程式碼上,還有待深入的學習。
而且是有兩種invoke方法即invoke和invokeSuper方法,所以使用的時候必須要謹慎。
結語
本文出自個人筆記,如有表述不當或者紕漏的地方歡迎指正。
與君共勉!!!
簡介
cglib是另外一種動態代理的方法,他和jdk動態代理的實現是有區別的,我們在之前見過jdk動態代理類是必須實現了介面的,而cglib不需要實現介面,但是必須保證類不含有final關鍵字,否則是無法代理的。
cglib案例
下面我們來展示一個cglib的死迴圈案例。首先是要被代理的類,還是和常規的一樣,宣告自己的方法就行,但是要確保類和方法沒有被final關鍵字修飾。用final關鍵字修飾類會直接報異常,但是修飾方法不會拋異常,但是此方法不會被代理,但是不影響其他方法被代理。
public class InfoDemo {
public void welcome (String person){
System.out.println("welcome :" + person);
}
}
複製程式碼
下面是具體的代理類實現
public class CglibInfoProxy implements MethodInterceptor{
private Object target;
public Object newInstance(Object source){
target = source;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method!!!");
Object value = methodProxy.invoke(o, objects);
//Object value = methodProxy.invoke(this.target, objects);
//Object value = methodProxy.invokeSuper(o, objects);
return value;
}
public static void main(String[] args) {
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
instance.welcome("zhangsan");
}
}
複製程式碼
和我們的jdk動態代理看起來十分相似,只是兩個類實現的介面不同,並且生成物件的方法也不同。這裡非常坑的是invoke
方法和invokeSuper
的區別,如果是用invoke方法一定要使用被代理的物件也就是上文中的target,而如果呼叫invokeSuper方法,則一定要使用被代理後的o物件。
上述這個例子就會引發死迴圈,導致StackOverflowFlow,嘿嘿,學沒有,棧溢位的場景
具體為什麼會這樣,可以先思考一下,後面我們在原始碼實現中再去講解。現在我們先看一下執行結果
...
before method!!!
before method!!!
before method!!!
Exception in thread "main" java.lang.StackOverflowError
at java.nio.CharBuffer.<init>(CharBuffer.java:281)
at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:70)
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.PrintStream.write(PrintStream.java:526)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at com.eumji.proxy.cglib.CglibInfoProxy.intercept(CglibInfoProxy.java:30)
at com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7.welcome(<generated>)
at com.eumji.proxy.cglib.InfoDemo$$FastClassByCGLIB$$2e560a7d.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
...
複製程式碼
此處只展示部分的效果,具體可以自己試一下。
假如我們換成其餘他兩條語句將會是正確的輸出,具體結果如下
before method!!!
welcome :zhangsan
複製程式碼
原理解析
要想弄清楚的這到底是怎麼回事,首先我們要看一下cglib代理後的類是怎樣的,要想生成代理類的檔案,我們只需要在我們的main方法中取消掉這句方法的註釋
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\classes");
就會在D盤的classes檔案下生成對應的代理class檔案,需要注意的是生成的代理class是有三個的,我們首先介紹一下我們最關心的InfoDemo代理類,其他的稍後合適的時機在描述其他兩個。
InfoDemo反編譯程式碼
其實就是將class檔案直接拖到IDEA中
public class InfoDemo$$EnhancerByCGLIB$$870a84d7 extends InfoDemo implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$welcome$0$Method;
private static final MethodProxy CGLIB$welcome$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$870a84d7");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.eumji.proxy.cglib.InfoDemo")).getDeclaredMethods())[0];
CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
}
final void CGLIB$welcome$0(String var1) {
super.welcome(var1);
}
public final void welcome(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
} else {
super.welcome(var1);
}
}
...
}
複製程式碼
看程式碼就可以看出來cglib還是很複雜的,現在我們暫且可以看一下我們關心的welcome方法,從上面的程式碼中可以看到cglib是會為被代理類的方法同時生成兩個代理方法的,一個是同名的welcome方法
和CGLIB$welcome$0方法
1.CGLIB$welcome$0方法
直接呼叫被代理的方法,也就是啥都沒幹。
2.welcome方法
首先判斷有沒有設定callback,很明顯我們在程式碼中有設定即為CglibInfoProxy,所以就會呼叫CglibInfoProxy.intercept方法。
本來想分析一波生成代理類的過程,看了一下有點複雜,暫時就不分析了。。。。
invokeSuper方法
前面也提及了invoke和invokeSuper方法稍不注意就會出問題的問題,在這裡我們從程式碼的層面去追蹤一下,產生問題的原因。
我們看一下代理類方法invokeSuper的執行流程
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init(); //初始化fastInfo
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
複製程式碼
invokeSuper在這裡主要的作用就是初始化fastClassInfo,然後通過fastClassInfo去呼叫目標方法。
init方法
此方法也有比較關鍵的地方,因為生成了代理類f1和f2。
private void init() {
if (this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
fci.f1 = helper(ci, ci.c1); //fastclass代理類生成
fci.f2 = helper(ci, ci.c2);//fastclass代理類生成
fci.i1 = fci.f1.getIndex(this.sig1); //獲取下標
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
複製程式碼
上面的方法主要是載入methodProxy.FastClassInfo。ci是之前就初始化好的,其中c1指的就是被代理的類InfoDemo,c2則是com.eumji.proxy.cglib.InfoDemo$$EnhancerByCGLIB$$efe38465
這個代理類。
然後生成對應的f1和f2以及方法的下標i1和i2,i1和i2對應的就是在最前面所說的welcome方法
和CGLIB$welcome$0方法
,後面程式碼可以看出。
而f1則是對應InfoDemo$$FastClassByCGLIB$$2e560a7d
代理類,f2則對應InfoDemo$$EnhancerByCGLIB$$efe38465$$FastClassByCGLIB$$38345933
代理類。這些都可以在生成的代理class中去檢視。
當然這裡並沒有說到底什麼生成的,有興趣的可以自己看一下位元組碼是怎麼生成的。個人沒太看懂。
invoke和invokeSuper區別
為什麼要生成兩個代理類f1和f2,我相信你看過之前的方法應該注意到了,在上面我們提及到了invoke方法和invokeSuper方法,我們來對比一下invoke方法和invokeSuper方法的區別
public Object invoke(Object obj, Object[] args) throws Throwable {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
}
複製程式碼
我們可以看到invoke使用的是f1.invoke方法,而invokeSuper則是使用f2.invoke方法。
首先看一下f1對應的invoke方法邏輯
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
InfoDemo var10000 = (InfoDemo)var2;
int var10001 = var1;
try {
switch(var10001) {
case 0:
var10000.welcome((String)var3[0]);
return null;
....
}
複製程式碼
直接呼叫InfoDemo物件的welcome方法。
所以這也就能解釋為什麼我們之前會發生迴圈呼叫invoke的方法了,因為我們傳入的var2是InfoDemo的代理物件,看最前面的代理類程式碼就可以看出,又會回到invoke方法,造成死迴圈。
再看一下f2中對應invoke的實現
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
efe38465 var10000 = (efe38465)var2;
int var10001 = var1;
try {
switch(var10001) {
....
var10000.CGLIB$finalize$1();
return null;
case 16:
var10000.CGLIB$welcome$0((String)var3[0]);
return null;
....
}
複製程式碼
因為我們此時我們傳入的var2是InfoDemo代理物件,所以最終會呼叫代理類中的CGLIB$welcome$0
方法。
小結
只是一次失敗的原始碼分析嘗試,不過弄清楚了造成呼叫死迴圈的原因,只能說cglib比jdk的動態代理複雜很多,主要體現在生成程式碼的邏輯和生成的程式碼上,還有待深入的學習。
而且是有兩種invoke方法即invoke和invokeSuper方法,所以使用的時候必須要謹慎。
結語
本文出自個人筆記,如有表述不當或者紕漏的地方歡迎指正。
與君共勉!!!