简单记录下,解决的一个问题,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技术 )