簡單記錄下,解決的一個問題,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代理的對象,因此就沒法實現自身調用加強;
該方法是 AopUtils的invokeJoinpointUsingReflection
而與之相反的則是,@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技術 )