动态代理——CGLIB动态代理原理&示例解析

觉得可以的话点个关注转个发呗,陆续奉上干货~~~~

前文我们讲解了JDK动态代理的原理(动态代理——JDK动态代理原理),今天我们来看看CGLIB动态代理是如何实现,最后我们总结下JDK动态代理和CGLIB动态代理的区别~~

先看下测试的源码:

动态代理——CGLIB动态代理原理&示例解析

输出:

inceptor-1---before invoke method: sayHello 
i am cglib
inceptor-1---after invoke method: sayHello

我们看下CGLIB生成的代理类(精简之后)

动态代理——CGLIB动态代理原理&示例解析

通过注释我们可以观察到代理类以及代理对象的实现方式:

  • 首先CGLIB使用ASM工具根据父类HelloServiceImpl及我们定义的方法拦截器MethodInterceptor生成代理CglibTest$HelloServiceImpl$$EnhancerByCGLIB$$69dcec54的字节码,然后将该字节码加载到虚拟机并根据静态代码块使用INIT方法初始化类中的类变量parentSayHelloMethod、sayHelloMethodProxy(及其他信息,截图中已省略其他信息,防干扰)
  • 实例化代理类CglibTest$HelloServiceImpl$$EnhancerByCGLIB$$69dcec54的对象,记为代理对象A,并伴随着代理类中的成员属性methodInterceptor初始化为我们定义的MethodInterceptor对象,因为我们的代理类继承了HelloServiceImpl,因此可以强转(HelloServiceImpl)A
  • 调用代理对象的sayHello方法:(HelloServiceImpl)A.sayHello(...)
  • 结合上图,我们看到调用sayHello实际上调用了我们定义的MethodInterceptor的methodInterceptor方法

动态代理——CGLIB动态代理原理&示例解析

接下来我们关注红框中的方法:

methodProxy.invokeSuper(proxyObj, args); // 7、执行父类对应方法

methodProxy这个对象是在生成的代理类的INIT方法中进行初始化的(上去看下图):

sayHelloMethodProxy = MethodProxy.create(parentClass, proxyClass, "方法描述", "sayHello", "cglibSayHello");

我们看下create方法:

动态代理——CGLIB动态代理原理&示例解析

接下来看invokeSuper方法:

动态代理——CGLIB动态代理原理&示例解析

在这个方法中,init方法用来初始化fastClassInfo这个属性对象,最终调用的是fci.f2.invoke方法。根据名字我们推断fastClassInfo是快速的,应该是用来优化性能的一个东西,我们回顾JDK动态代理的实现方式时,我们在invoke方法中是根据method对象反射进行调用的,我们知道反射的性能相对较低,那么fastClassInfo这个对象是怎么优化的呢?

我们来看下init方法,为了说明init方法的意思,写了两个类(真实的这两个类也是CGLIB动态生成的,内部为每个方法描述生成了唯一的索引并使用switch表达式进行分支控制,本例为了简单说明是使用if语句根据方法名称进行判断,理解意思即可

class HelloServiceImpl$$FastClass{
    public Object invoke(int index, HelloServiceImpl obj, Object[] args){
        if (index == "sayHello".hashCode()){
            return obj.sayHello((String)args[0]);
        }else {
            // ....其他方法
        }
        return null;
    }
}
class Proxy$$FastClass{
    public Object invoke(int index,  ProxyClass proxy, Object[] args){
        if (index == "cglibSayHello".hashCode()){
            return proxy.cglibSayHello((String)args[0]);
        }else {
            // ....其他方法
        }
        return null;
    }
}
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1); // f1指向HelloServiceImpl$$FastClass的一个实例
fci.f2 = helper(ci, ci.c2); // f2指向Proxy$$FastClass的一个实例
fci.i1 = fci.f1.getIndex(sig1); // 生成父类sayHello方法的一个唯一索引
fci.i2 = fci.f2.getIndex(sig2); // 生成代理类cglibSayHello方法的一个唯一索引
fastClassInfo = fci;
createInfo = null;

完成上面的初始化后我们进入到这个方法的分析:

return fci.f2.invoke(fci.i2, obj, args);

f2指向Proxy$$FastClass实例(上面的注释),调用的就是proxy.cglibSayHello((String)args[0])这个方法,我们再回到最开始生成的代理类中,查看cglibSayHello方法,调用的是父类的sayHello方法:

final String cglibSayHello(String var1) {
    return super.sayHello(var1);
}

这样就形成了一个闭环~~~~

因此CGLIB在这一部分的优化就是用了判断的形式直接调用的对象的方法,付出的代价是多加载了几个类到虚拟机中,是一种空间换时间的一种思想;而JDK的方式是使用方法对象进行反射调用,节省了空间但降低了效率

总结JDK动态代理和CGLIB动态代理:(两种实现方式大体思路基本相同)

JDK动态代理:

  • 需要目标类实现接口
  • 生成的代理类是与目标类平级,实现了共同的接口
  • 使用反射的方式进行最终方法的调用,性能较低

CGLIB动态代理:

  • 不要求目标类实现接口
  • 生成的代理类是目标类的子类
  • final方法不会出现在代理类中
  • 使用空间换时间的思想对最终的方法调用进行了优化,提升了运行时优化

把代码的意思用文字表述出来,一不小心就感觉有点乱,不知道大家能不能看懂~~~欢迎留言~~~

觉得可以的话点个关注转个发呗,陆续奉上干货~~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值