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

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

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

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

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

 

 

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

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

    接下来分别解释一下

 

invokeSuper

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

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

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

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

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

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

 

invoke

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

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

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

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

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

### 比较JDK动态代理CGLIB动态代理 #### 实现原理 JDK动态代理主要依赖于`java.lang.reflect.Proxy`类以及`InvocationHandler`接口来实现代理功能。通过这种方式,可以在运行时为一组接口创建代理实例,并且能够拦截这些接口的方法调用[^1]。 对于CGLIB动态代理而言,则是利用ASM字节码框架,在内存中实时生成目标类的子类,并重写其中的方法以达到增强业务逻辑的目的。由于采用的是继承的方式,因此被代理的对象不需要实现任何接口也可以完成操作[^2]。 #### 性能特点 通常来说,JDK动态代理因为只涉及到了反射层面的操作,所以在大多数情况下其执行效率会高于CGLIB动态代理;但是当涉及到大量方法调用或者频繁创建销毁代理对象的时候,两者之间的差距可能会缩小甚至反转。另外值得注意的一点是,如果应用程序中有大量的final修饰符限定的方法存在的话,那么使用CGLIB将会受到一定限制,因为它无法覆盖此类成员函数[^3]。 #### 适用场景 - **JDK动态代理** 更适合用于那些已经定义好了服务契约(即接口)的应用程序组件之间通信的地方。只要所有的参与者都遵循相同的协议标准即可正常工作。此外,由于它不改变原有代码结构,所以维护起来相对容易一些。 - **CGLIB动态代理** 当面对没有提供公共接口的具体类型时显得尤为有用。比如某些第三方库中的核心实体类可能并没有给出相应的抽象层次供外部扩展。此时借助于CGLIB就可以绕过这个问题轻松解决问题。不过需要注意的是,该方案并不适用于最终(final)类型的处理。 ```java // 使用JDK动态代理的例子 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxyExample { public static void main(String[] args) { MyInterface target = new TargetClass(); InvocationHandler handler = (proxy, method, arguments) -> { System.out.println("Before calling " + method.getName()); Object result = method.invoke(target, arguments); System.out.println("After calling " + method.getName()); return result; }; MyInterface proxyInstance = (MyInterface) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class[]{MyInterface.class}, handler); proxyInstance.someMethod(); } } interface MyInterface {void someMethod();} class TargetClass implements MyInterface{public void someMethod(){}} ``` ```java // 使用CGLIB动态代理的例子 import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyExample { public static void main(String[] args){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetClassForCglib.class); // 设置父类 enhancer.setCallback(new MethodInterceptor(){ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable{ System.out.println("Before calling "+method.getName()); Object result=proxy.invokeSuper(obj,args); System.out.println("After calling "+method.getName()); return result; }}); TargetClassForCglib cglibProxy=(TargetClassForCglib)enhancer.create(); cglibProxy.someMethod(); } static class TargetClassForCglib{ public void someMethod(){} } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值