Cglib invoke以及invokeSuper的一點區別

簡單記錄下,解決的一個問題,Cglib的invoke和invokeSuper的區別:html

  簡而言之,invoke方法調用的對象沒有加強過,invokeSuper方法調用的對象已是加強了的,因此會再走一遍 MyMethodInterceptor的 interceptor方法,若是是個攔截器鏈條,就會從新在走一次攔截器鏈;java

一。準備環境 Gglib的兩個jar包,由於Cglib使用了ASM生成子類;api

二。代碼準備jvm

public class Target { public void a() { System.out.println(" a 方法"); } public void b() { System.out.println(" b 方法"); } }

 

import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    //obj是代理後的子類 ,method是調用方法 ,args是方法入參 , proxy是MethodProxy代理對象 System.out.println(
"myMethodInterceptor go "); Object res = proxy.invokeSuper(obj, args); return res; } }

 

測試類:ide

public class TestApp { public static void main(String[] args) { Enhancer e = new Enhancer(); e.setSuperclass(Target.class); e.setCallback(new MyMethodInterceptor()); Target t=(Target) e.create(); t.a(); } }

 

測試結果:工具

myMethodInterceptor go 
 a 方法

 

 

三。測試

3.1  先解決一個問題,Target這個類裏面方法寫 this 就是 指的生成的Cglib子類 ,this

測試在a方法中添加一句輸出this   ,結論:Cglib代理的時候target對象中的this就是Cglib子類  (你可能以爲我說的是廢話,子類對象在父類的this指的不是自身嗎? 你知道Spring Aop裏this方法沒法加強自身調用,這時候你就開始懷疑人生了)spa

 

3.2 既然知道了this對象就是指代的自身,那我好比 this.b() 或者 b() 應該也被回調一次了 。.net

public class Target { public void a() { System.out.println(" a 方法"); b(); } public void b() { System.out.println(" b 方法"); } }

 

 

其餘類不改動代碼,測試結果以下:  果真 this.b()方法也被加強了;

myMethodInterceptor go 
 a 方法
myMethodInterceptor go 
 b 方法

 

你在 b()  打個斷點,下一步就跳進入 MyMethodInterceptor 的 intercept 方法裏了 ;這個彷佛也沒有毛病,其實緣由就是 invokeSuper;invokeSuper傳入的參數是Cglib代理的子類 ,就至關於 調用 Target$$EnhanceredByCGLIB這個子類的b()方法,確定會再次進入回調;

 

3.3 如何實現像AOP同樣 調用自身沒法加強呢?

修改代碼以下: 改動的地方已經標紅了 :)

public class Target { public void a() { System.out.println(" a 方法"); b(); } public void b() { System.out.println(" b 方法"); } } public class MyMethodInterceptor implements MethodInterceptor{ private Object target; public MyMethodInterceptor(Object target) { super(); this.target = target; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("myMethodInterceptor go "); // Object res = proxy.invokeSuper(obj, args);
        Object res = proxy.invoke(target, args); return res; } } public class TestApp { public static void main(String[] args) { // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\api");
        Target target = new Target(); Enhancer e = new Enhancer(); e.setSuperclass(Target.class); e.setCallback(new MyMethodInterceptor(target)); Target t=(Target) e.create(); t.a(); } }

 

 

測試結果以下:

myMethodInterceptor go 
 a 方法
 b 方法

 

這就和AOP的功能一毛同樣了吧 ;  區別就在於 invoke 和 invokeSuper : 在我理解看來,invoke方法調用的對象沒有加強過,invokeSuper方法調用的對象已是加強了的,因此會再走一遍 MyMethodInterceptor的 interceptor方法,若是是個攔截器鏈條,就會從新在走一次攔截器鏈;

 

四。

查看下Spring CGLIB的Aop  ,  這個就是執行完 環繞通知 、 前置通知 以後執行業務方法的地方 ,target對象存的是原生的bean,沒有被CGLIB代理的對象,因此就沒法實現自身調用加強;

該方法是 AopUtilsinvokeJoinpointUsingReflection 

 

而與之相反的則是,@Configuration註解下類中 @Bean註解標註方法裏的 方法調用,獲得的是同一個@Bean對象;

由於 BeanMethodInterceptor 的 interceptor方法  調用的invokeSuper方法 ,好比 getMan2方法調用getMan方法,那個getMan方法調用的是 CGLIB子類的getMan方法 ,此時getMan是加強後的getMan方法,這時候就會檢測ThreadLocal當前線程和當前方法是否一致了,不一致嘗試從容器中獲取該bean對象                                      戳我查看原文

 

五。查看Cglib生成子類的方案思路

方案一。

測試類上加上這樣一句話:

public class TestApp { public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\api"); Target target = new Target(); Enhancer e = new Enhancer(); e.setSuperclass(Target.class); e.setCallback(new MyMethodInterceptor(target)); Target t=(Target) e.create(); t.a(); } }

 

 看到控制檯輸出這樣的:

CGLIB debugging enabled, writing to 'E:\api' myMethodInterceptor go a 方法 b 方法

 能夠看到確實生成了CGLIB子類class文件;

 

個人class文件經過JD-GUI查看倒是有些問題  有的地方有不少label ,有知道的怎麼解決的評論告訴我 多謝 :)

 public final void a() { MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0; if (tmp4_1 == null) { tmp4_1; CGLIB$BIND_CALLBACKS(this); } if (this.CGLIB$CALLBACK_0 != null) return; super.a(); }

 

 

方案二。

我以爲是個很神奇的地方,方便之後查看  ,附上做者原文連接,無抄襲的意思  https://blog.csdn.net/lzufeng/article/details/79322391

命令行輸入

 java -classpath "D:\Java\jdk1.8.0_181\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB

 

 會彈出來一個java工具,選擇 File -- > Attach to HotSpot process ,會要求輸入進程號

查看方式 新開一個命令行輸入

jps -l

  能夠看到 13592 就是咱們須要的;輸入剛纔的java小工具中,

選擇Tool --> Class Browser ,在輸入框輸入以前的類  Target類

選中下面的CGLIB的子類, 選擇Create .class File

 

文件就生成成功了,找到這個class文件方式不少  ,我測試的時候是在 第一個命令行當前目錄下面找到的  ; 此外還能夠電腦上文件搜索那個文件名;

 

(方案三。提供一種思路,能夠忽略,由於我也不是很瞭解這個技術 ,java  探針技術 仍是 agent技術 )

相關文章
相關標籤/搜索