Cglib invoke以及invokeSuper的一點區別


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

  簡而言之,invoke方法調用的對象沒有增強過,invokeSuper方法調用的對象已經是增強了的,所以會再走一遍 MyMethodInterceptor的 interceptor方法,如果是個攔截器鏈條,就會重新在走一次攔截器鏈;

一。准備環境 Gglib的兩個jar包,因為Cglib使用了ASM生成子類;

二。代碼准備

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; } }

 

測試類:

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子類 ,

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

 

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

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技術 )

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM