cglib动态代理中invokeSuper和invoke的区别

转自:

https://mp.weixin.qq.com/s?__biz=MzUzNzA2MzUzMg==&mid=2247483681&idx=1&sn=aecb8cab514893c645e8344a49bf982e&chksm=faedf822cd9a7134634d01e585dcca259869a5bfc35995f855ce8a489e470ba3d94c52f05dac&token=693517720&lang=zh_CN#rd

用invoke()方法会造成栈溢出的现象

首先需要先说一下FastClass。在使用Cglib动态代理时会生成3个新类,如下图所示。

    第一个文件:代理类的FastClass类

    第二个文件:代理类,继承自被代理类

    第三个文件:被代理类的FastClass类

 

cglib动态代理中invokeSuper和invoke的区别

 

    FastClass类主要实现了字节码与执行方法的索引关系。大致果然如下图所示,“字符串”与“对象”分别的两个过程的主要入参。cglib代理以这种方式避免了对被代理对象的反射调用,这也是cglib性能较优于JDK代理的地方。

cglib动态代理中invokeSuper和invoke的区别

    但是在MethodInterceptor拦截方法中却发现有两种调用方式一种是invokeSuper,另一种是invoke,这两种后台源码看起来差不多。排除异常处理部分,主要不一样的地方在于:一个是用的fci.f1.invoke(#),一个是用的fci.f2.invoke(#)。

cglib动态代理中invokeSuper和invoke的区别

    接下来分别解释一下

 

invokeSuper

    首先是invokeSuper,对其正确的使用大致如下图所示

cglib动态代理中invokeSuper和invoke的区别

    这里要明确说明invokeSuper的入参中,sub代表代理类对象,也就是intercept方法的第1个入参为代理类对象。invokeSuper调用前后的主要流程如下图所示

cglib动态代理中invokeSuper和invoke的区别

    假设前端触发了方法名为fun调用,首先进入代理对象中的同名方法,然后进入自定义的方法拦截对象MethodInterceptor,图中的this也就是代理对像,在调用invokeSuper后,cglib内部会通过代理类的FastClass找到要执行的方法,这会传到FastClass中的对象是代理对象,所以在通过FastClass对象后,流程会重新走到代理对象中,但此时不会再次调用fun同名方法,而是调用了一个CGLIB$fun$1的方法,该方法会调用父类(也就是被代理类)fun方法,至此流程结束。

    如果将MethodInterceptor中的invokeSuper改为invoke,会怎么样呢?会出现*Error,同时invoke之前的内容会被反复执行。请看如下流程图

cglib动态代理中invokeSuper和invoke的区别

    当执行invoke时,invoke内部逻辑使用的是被代理类的FastClass,所以最后找到的方法名是fun,但是关传入invoke的对象依然是代理对象。所以经过FastClass后又回到了代理对象的fun方法,与入口的调用是同一个,然后会被继续拦截,如此反复。就如上图中红色箭头所示。

    其实代理对象生成后,会产生一个同名的fun方法用于前端调用,该方法内部会将请求传递到拦截对象中。除此之外,代理对象中还会生产一个CGLIB$fun$1方法,该方法会调用父类(被代理对象)的fun方法。在使用invokeSuper的过程中,会经拦截器、FastClass走到CGLIB$fun$1这里,然后在CGLIB$fun$1中请求父类的fun方法。大致过程如下图所示

cglib动态代理中invokeSuper和invoke的区别

 

invoke

    invoke又该如何使用呢?上面已经提到当使用invoke时,会通过被代理类的FastClass进行索引定位,所以这时只要把被代理对象传进来即可。程序大致如下图所示。

cglib动态代理中invokeSuper和invoke的区别

    obj为被代理l类的对象,这个需要在MethodInterceptor初始化时进行设置。使用invoke的大致前后流程如下图所示。

cglib动态代理中invokeSuper和invoke的区别

     通过被代理对象的FastClass得到的是被代理对象的执行方法名。由于这时传入FastClass的是被代理对象,所以程序会直接执行被代理对象的fun方法,不再回到代理对象中。

    注:为什么之前使用invoke时传入sub(代理对象)不报对象类型错误,是因为代理对象继承自被代理对象,所以sub是可以被强制转换成被代理对象,所以没有报对象类型错。

    最后,如果所有指定类的对象都需要被代理,可以使用invokeSuper,省去了创建和设置被代理类的对象。如果被代理类的对象一部分需要代理,一部分不需要被代理,可以考虑使用invoke。实际使用哪种,还需要根据程序前后的设计来定夺。

 

上一篇:动态代理最全详解系列[5]-Cglib动态代理源码分析


下一篇:mica cglib 增强——[1]cglib bean copy 介绍