簡單記錄下,解決的一個問題,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代理的對象,所以就無法實現自身調用增強;
該方法是 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技術 )