静态代理、jdk动态代理、cglib动态代理傻傻分不清?

开篇

上篇文章结束的时候给大家留了一个问题,那就是大家还记得Spring AOP是通过什么技术实现的吗?

现在我们就来揭晓答案,其实Spring AOP是通过动态代理来进行实现的,动态代理呢,它是代理模式中的一种实现而已,那么代理模式还有哪些实现呢?这些实现具体又是怎么来玩的呢?通过这篇文章大家都可以一一找到答案的。

为了让大家可以更好的搞明白动态代理,也为了照顾一些0基础的同学,我们会从最基础的代理模式开始聊起,主要从这几个方面进行讲解:

1首先我们会先来看下代理模式到底是什么?
2然后来看下静态代理是一个什么样的玩儿法?
3接着来看下动态代理中的jdk代理是怎么来玩儿的?
4最后会看下动态代理中的cglib代理是怎么来玩儿的?

代理模式是什么?

首先我们来看一下代理模式中有哪些角色,我们开看下这张简单的图



可以看到,在代理模式中主要有客户端、代理类、目标类这3个角色,它们的含义如下:

●客户端:就是用来发起调用请求的,没啥好说的
●目标类:特定行为的实现类,真正“干活”的角色
●代理类:对外代理目标类所有的功能,在目标类功能的基础上会做一些额外的工作,通过在代理类内部持有目标对象引用实现

那么代理模式是怎么来玩的呢?

其实很简单,就和上边那张图一样,开始的时候客户端会来调用代理类的方法,由于在代理类内部持有目标对象的引用,此时代理类会直接调用目标对象的方法来完成这个功能,同时呢,代理类还可以自己添加一些增强逻辑,做一些额外的功能。

好了,现在代理模式我们已经搞清楚了,并且我们知道动态代理只是代理模式的其中一种实现方式,那么代理模式还有哪些实现方式呢?我们看这张图



其实呢,代理模式分为两大类,分别是静态代理和动态代理,而动态代理主要分为jdk动态代理和cglib动态代理,所以简单来说,代理模式我们常用的实现方式分别是静态代理、jdk动态代理、cglib动态代理这三种,它们都可以实现在目标对象功能的基础上,添加一些增强逻辑,也就是做一些额外的工作。

那么静态代理、jdk动态代理、cglib动态代理它们都是怎么实现的?具体都是怎么来玩儿的呢?

大家不要着急,接下来我们就分别来玩儿一下静态代理、jdk动态代理、cglib动态代理,看下它们到底是怎么来玩的。

静态代理怎么玩儿?

静态代理之所以有“静态”两个字,是因为静态代理中的代理类在编译期间就创建好了,不是通过编译器生成的代理类,而是我们自己手动创建出来的类。

所以要玩儿静态代理,那么我们就需要手动来创建一个代理类,比如下边这个代理类



这个ProductServiceProxy就是我们手动创建出来的代理类,我们可以看到它内部持有一个目标对象的引用target,需要注意的是这个目标对象target和代理类ProductServiceProxy是同一种接口类型,它们都是ProductService接口类型,所有它们有相同的方法

然后我们可以看到在ProductServiceProxy的addProduct()和getProductById()方法中,都直接调用目标对象target相应的方法来完成了功能,就类似于“请求转发”一样,同时在调用目标对象target方法的前边和后边都添加了增强逻辑,就是分别打印入参和出参。

这就是静态代理的玩儿法,其实是非常简单的,那接下来我们来看下jdk动态代理和cglib动态代理怎么玩儿吧

jdk动态代理怎么玩儿?

在讲jdk动态代理之前,大家先思考下,既然有了静态代理,那为啥还要有动态代理呢?

换种说法就是,现在静态代理存在什么问题?动态代理又是怎么解决这些问题的?

其实很简单,以我们刚手动创建的静态代理类ProductServiceProxy为例,它里边有两个方法,分别是addProduct()和getProductById(),然后我们分别在这两个方法中添加了增强逻辑,也就是日志代码片段。那如果这个代理类ProductServiceProxy中有20个方法呢?难道我要在20个方法中一个一个的添加增强逻辑吗?

这样做当然是可以的,但就是维护成本太高了,因为我们发现这些增强逻辑(日志代码片段),其实都是些重复代码,那么有没有一种更友好的方式,来进行统一处理呢?

那当然是有的,这种方式就是动态代理了,和静态代理不同的是,静态代理的代理类在代码运行前已经被我们手动创建好了,并且已经生成了class文件,而动态代理则是在程序运行时才创建的代理类,也就是说动态代理中的代理类不是我们手动在java代码中定义的,而是在运行的时候“动态”生成的。

而动态代理带来的好处就是可以对代理类中所有的方法进行统一处理,这样就可以解决代码重复以及维护成本过高的问题了,非常的灵活。

那动态代理怎么玩呢?我们以jdk动态代理为例,我们看这块代码



我们可以看到这个JdkDynamicProxy实现了InvocationHandler接口,需要注意的是这个JdkDynamicProxy可不是代理类,真正的代理类在代码运行时通过getProxy()方法生成,而getProxy()方法中其实是调用了jdk的Proxy.newProxyInstance()方法来生成代理的,其中传入的参数分别为目标对象的类加载器、目标对象的接口类型以及回调程序,回调程序其实就是它自己,也就是JdkDynamicProxy。

也就是说当调用代理类的方法时,就会来回调这个JdkDynamicProxy类了,说的具体点就是会回调到JdkDynamicProxy类的invoke()方法。

说白了就是对代理类所有方法的调用,都会回调到这个invoke()方法,此时这个invoke()方法就会通过反射来调用目标对象的方法了,同时我们可以在调用目标对象方法之前和之后加一些增强逻辑,比如记录日志。

简单说这个invoke()方法就相当于统一的收口,我们可以在这里加一些通用的增强逻辑,这样静态代理重复代码和维护成本高的问题就都解决了。

cglib动态代理怎么玩儿?

最后我们来看一下cglib动态代理,好,那我们先来思考一下,既然有了jdk动态代理,那为啥还要有cglib动态代理呢?

是这样的,我们细心点就会发现,刚才在创建jdk动态代理时会传入三个参数,其中有一个就是目标对象的接口类型,其实jdk动态代理是基于接口实现的,所以创建代理的时候,需要指定一下生成的代理类需要实现哪些接口。

那现在问题来了,如果有一个类没有实现接口,此时jdk动态代理是无能为力的,因为它是基于接口实现的,可我们就是想为这个类创建代理怎么办?

那我们可以使用另外一种方式,那就是cglib动态代理,cglib动态代理是基于继承实现的,即使目标类没有实现接口也是可以正常生成代理的,我们看下边的代码:

我们可以看到在CglibDynamicProxy类中有一个getProxy()方法,而在这个getProxy()方法中构建了一个字节码增强器实例enhancer,同时为enhancer设置了生成代理类的父类类型和回调方法,因为Cglib的原理是动态生成要代理类的子类,然后子类去重写父类的方法,所以这里要设置生成代理类的父类类型,最后调用了enhancer.create()方法来创建代理。

当调用代理中的方法时,就会回调到这个intercept()方法,这个intercept()方法和jdk动态代理中的invoke()方法是一样的,说白了就是一个统一的收口,我们可以在这里添加一些统一的增强逻辑,比如统一记录方法的入参和出参。

这个就是cglib动态代理的玩儿法,其实和jdk动态代理的玩儿法差不多,只不过jdk动态代理是基于接口实现,而cglib动态代理是基于继承,子类重写父类方法罢了。

最后让我们来一起测试下静态代理、jdk动态代理、cglib动态代理的代码吧,看到底能不能实现统一记录日志的功能。

测试一下

测试类很简单,代码如下:



可以看到静态代理直接将目标对象target通过构造方法传递进去,构建出来了代理对象staticProxy

而jdk动态代理,也是先将目标对象target通过构造方法传递进去,然后通过getProxy()方法完成了代理的创建,最后将代理对象强转为了接口类型ProductService,由于jdk动态代理是基于接口实现的,生成的代理类会实现这个ProductService接口,所以是可以这样强转的。

最后cglib动态代理,同样是先将目标对象target通过构造方法传递进去,然后通过getProxy()方法完成了代理的创建,只不过这里是将代理对象强转为了ProductServiceImpl类型,因为cglib是基于继承来的,生成的代理类本质是ProductServiceImpl类的子类,所以这里是可以强转为ProductServiceImpl类型的。

最后分别调用了代理对象中的方法,打印结果如下:


可以看到,静态代理、jdk动态代理以及cglib动态代理都按照预期完成了记录日志的功能。

通过本篇文章对静态代理、jdk动态代理以及cglib动态代理的介绍,以及代码实操,相信大家对代理模式这块会有更深的体会,同时也为后边剖析Spring AOP的源码奠定了坚实的基础。

总结

好了,今天的知识点,我们就讲到这里了,我们来总结一下吧。

第一,代理模式分为两大类,分别是静态代理和动态代理,而动态代理又分为了jdk动态代理和cglib动态代理。

第二,我们介绍了它们各自的特点和实现方式,同时也做了代码实操,希望大家看完文章后可以亲自动手写代码感受一下,相信会给你带来不一样的体会。

最后给大家留一个小思考题:那就是既然AOP是通过动态代理实现的,那么这个动态代理对象到底是在哪个阶段生成的呢?大家可以先思考一下,下一篇文章我们会一起来探索一下。

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值