利用代码分别实现jdk动态代理和cglib动态代理_代理模式实现方式及优缺点对比...

作者:爱宝贝丶来源:https://my.oschina.net/zhangxufeng/blog/1633187

代理模式最典型的应用就是AOP,本文结合主要讲解了代理模式的几种实现方式:静态代理和动态代理,这里动态代理又可以分为jdk代理和Cglib代理,另外,本文也对这几种代理模式的优缺点进行了对比。

代理,顾名思义,即代替被请求者来处理相关事务。代理对象一般会全权代理被请求者的全部只能,客户访问代理对象就像在访问被请求者一样,虽然代理对象最终还是可能会访问被请求者,但是其可以在请求之前或者请求之后进行一些额外的工作,或者说客户的请求不合法,直接拒绝客户的请求。如下图所示为代理模式的一份简图:

3d82268ad5a22df7983ab2d673d5786c.png

代理模式的角色:

  • ISubject:代理者与被代理者共同实现的接口,可以理解为需要代理的行为;
  • SubjectImpl:被代理者,其为具有某种特定行为的实现者;
  • SubjectProxy:代理者,其会全权代理SubjectImpl所具有的功能,在实现其功能的基础上做一些额外的工作;
  • Client:客户端,客户端访问代理者与访问被代理者具有类似的效果,其无法区分访问的是代理者还是被代理者。

1. 静态代理

静态代理模式也即上图中描述的这种模式,从图中可以看出,SubjectProxy保存一个ISubject实例,当客户端调用SubjectProxy的request()方法时,其除了做额外的工作之外,还会调用ISubject实例的request()方法。如下是这三个类的一个简单实现:

87cc173fd0f0ab30452ecbd4ae309b3a.png

可以看到,代理对象在调用被代理对象的方法之前和之后都打印了相关的语句。如下是客户端请求示例:

public class Client { @Test public void testStaticProxy() { ISubject subject = new SubjectImpl(); ISubject proxy = new SubjectProxy(subject); proxy.request(); }}

运行上述用例,可得到如下结果:

before safety check.request SubjectImpl.after safety check.

从客户端访问方式可以看出,客户端获取的是一个实现ISubject接口的实例,其在调用的request()方法实际上是代理对象的request()方法。这种代理方式称为静态代理,并且这种代理方式也是效率最高的一种方式,因为所有的类都是已经编写完成的,客户端只需要取得代理对象并且执行即可。

静态代理虽然效率较高,但其也有不可避免的缺陷。可以看到,客户端在调用代理对象时,使用的是代理对象和被代理对象都实现的一个接口,我们可以将该接口理解为定义了某一种业务需求的实现规范。如果有另外一份业务需求(如进行数据修改),其与当前需求并行的,没有交集的,但是其在进行正常业务之外所做的安全验证工作与当前需求是一致的。如下是我们进行该数据修改业务的实现代码:

fdcf5b0c45f051ee471514e95edebf09.png

如下是客户端代码:

public class Client { @Test public void testStaticProxy() { ISubject subject = new SubjectImpl(); ISubject proxy = new SubjectProxy(subject); proxy.request();  IUpdatable updatable = new UpdatableImpl(); IUpdatable proxy = new UpdatableProxy(updatable); proxy.update(); }}

可以看到,要实现相同的对象代理功能(安全验证),静态代理方式需要为每个接口实现一个代理类,而这些代理类中的代码几乎是一致的。这在大型系统中将会产生很大的维护问题。

2. 动态代理

① jdk代理

所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:InvocationHandler和Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可,如下是实现该接口的一个示例:

35b36fd0126d4e02ee7eae7b275cb168.png

如下是客户端调用方式:

public class Client { @Test public void testDynamicProxy() { ISubject subject = new SubjectImpl(); ISubject proxySubject = (ISubject) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{ISubject.class}, new SafetyInvocationHandler(subject)); proxySubject.request(); IUpdatable updatable = new UpdatableImpl(); IUpdatable proxyUpdatable = (IUpdatable) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IUpdatable.class}, new SafetyInvocationHandler(updatable)); proxyUpdatable.update(); }}

可以看到,客户端在调用代理对象时使用的都是同一个SafetyInvocationHandler。这里jdk代理其实在底层利用反射为每个需要代理的对象都创建了一个InvocationHandler实例,在调用目标对象时,其首先会调用代理对象,然后在代理对象的逻辑中请求目标对象。这也就是为什么在代理类中可以保存目标对象实例的原因,比如上述的SafetyInvocationHandler,其声明了一个Object类型的属性用来保存目标对象的实例。

jdk代理解决了静态代理需要为每个业务接口创建一个代理类的问题,虽然使用反射创建代理对象效率比静态代理稍低,但其在现代高速jvm中也是可以接受的,在Spring的AOP代理中默认就是使用的jdk代理实现的。这里jdk代理的限制也是比较明显的,即其需要被代理的对象必须实现一个接口。这里如果被代理对象没有实现任何接口,或者被代理的业务方法没有相应的接口,我们则可以使用另一种方式来实现,即Cglib代理。

② Cglib代理

Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象实现某个接口的问题。对于需要代理的类,如果能为其创建一个子类,并且在子类中编写相关的代理逻辑,因为“子类 instanceof 父类”,因而在进行调用时直接调用子类对象的实例,也可以达到代理的效果。Cglib代理的原理实际上是动态生成被代理类的子类字节码,由于其字节码都是按照jvm编译后的class文件的规范编写的,因而其可以被jvm正常加载并运行。这也就是Cglib代理为什么不需要为每个被代理类编写代理逻辑的原因。这里需要注意的是,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写。如下是使用Cglib代理的一个示例:

/** * 被代理类 */public class Suject { public void request() { System.out.println("update without implement any interface."); }}/** * 代理类 */public class SafetyCheckCallback implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before safety check."); Object result = methodProxy.invokeSuper(o, objects); System.out.println("after safety check."); return result; }}

如下是客户端访问方式:

public class Client { @Test public void testCglibProxy() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Suject.class); enhancer.setCallback(new SafetyCheckCallback()); Suject proxy = (Suject) enhancer.create(); proxy.request(); }}

可以看到,客户端代码中首先创建了一个Enhancer对象,并且设置了父类及代理回调类对象。该Enhancer对象会为目标类创建相关的子类字节码,并且将代理代码植入该子类字节码中。

3. 总结

本文主要对代理模式的三种实现方式进行了详细讲解,并且比较了各个代理方式的优缺点,Spring主要使用的是动态代理方式实现切面编程的。这里读者可能会有一个疑问,即上述代理代码中,根据实现方式的不同,对客户端代码都有一定的侵入性,比如静态代理客户端需要侵入代理类的实例,jdk代理需要侵入Proxy类,而Cglib代理则需要侵入子类子类对象创建等代码。理论上,客户端只需要获取目标对象,无论是否为代理过的,然后调用其相关方法实现特定功能即可。这其实也是工厂方法的强大之处,因为工厂方法会将对象的创建封装起来,对象的具体创建过程可以根据具体的业务处理即可,客户端只需要依赖工厂类调用相关的方法即可。同样的这也就说明了Spring IoC容器是天然支持AOP代理的原因,因为其将对象的创建过程交由容器进行了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防抖 节流_防抖节流与前端性能优化

在我们日常的开发中经常会用到一些容易被反复触发的事件。比如:scroll、resize、鼠标事件(mousemove,mouseover等)、键盘事件(keyup、keydown)。频繁触发回调导致的大量计算会引发页面的抖动甚至卡顿。为了规避这种情况,我们需要一些手段来控制事件被触发…

惠普10代的服务器有哪些型号,英特尔官方科普:秒懂十代酷睿型号怎么认!

今日,英特尔官方微博再次放出科普:十代酷睿处理器是如何命名的?英特尔介绍,以酷睿i7-1065G7为例,“i7”为产品型号,“1065”中的“10”代表十代酷睿,“65”为CPU代号,“G7”为显卡性…

form提交后台注解拿不到数据_浏览器是如何将用户数据发送到服务器的?

今天是刘小爱学习Java的第89天。感谢你的观看,谢谢你。话不多说,开始今天的学习:在学习之前,先思考如下问题:对于浏览器来说:如何将用户数据发送到服务器呢?数据传输的格式是怎么样的呢&#xf…

proxmoxve打造云桌面_微软云电脑Cloud PC曝光:配置一般还不便宜

光纤宽带的普及和提速、5G的兴起,让云电脑、云游戏、云手机等产品和应用红火起来。而微软也正在开发一款名为Cloud PC的云端Windows操作系统,并计划2021年正式推出。据报道,Cloud PC是由Azure云服务支撑,基于虚拟桌面打造的&#…

alpine linux图形界面_跟光磊学Linux运维-Linux入门与基本使用

认识Linux用户在安装CentOS8.2时,设置过root用户的密码,同时也创建了用户guanglei。其中root用户是系统自带的管理员账户,也被称为超级用户,root用户接近系统完整的控制能力,对系统损害几乎有无限的能力。运维人员在生…

休眠 嵌入式_内幕消息:嵌入式软件挤出最低功耗模式

低功耗运行仍然是各行业应用的关键驱动因素。随着睡眠模式的增加,电源管理突然从单纯的硬件问题转移到软件开发人员必须考虑的事情上。功耗模式的最简单应用是当系统空闲时,将其置于休眠状态。然而,今天的MCU提供多种低功耗模式,进…

cnn 验证集 参与训练吗_一个简单的零基础的机器学习教程之二,字母数字验证码识别...

一.前言基于前面我发的贴子 土味程序员:一个简单的零基础的机器学习教程,Pytorch搭建Faster R-CNN目标检测平台​zhuanlan.zhihu.com一个非常震撼的目标检测的例子。上个帖子从环境安装到调试代码再到图片检测视频检测一个详细的教程,今天我来…

activiti 文档_免费、开源、多平台的PDF文档处理软件——PDFsam Basic

今天给大家推荐的是一款免费、开源、多平台支持的PDF文档处理软件——PDFsam BasicPDFsam Basic是为普通用户提供的免费开源解决方案,提供了PDF文档拆分、合并、混合、提取页面和旋转等等功能。01. 文档分割PDFsam Basic可以通过给定页码、书签级别,把PD…

@data注解不生效_你说啥什么?注解你还不会?

点击蓝色字免费订阅,每天收到这样的好信息前言:最近有不少粉丝关注本公众号。并且我已经成功开通了流量主同时会赚一点点广告费,我打算每个月把这部分钱拿出来给大家买点书刊,算是给大家一点福利吧。大家想买什么书扫描下方的加他拉你加群。最后,非常感谢大家的关注…

yearning 2. 部署_对于企业来说,在选择协同办公系统的时候,选择私有化部署的数据安全一些,还是使用云服务器比较安全?...

当然是私有化部署!因为它除了安全,还有个性化~私有化部署,简单理解就是企业自己购买或租赁服务器,或者由服务商提供免费的云资源,然后将整个系统部署在企业自有的服务器上。采用这种方式,企业就不用担心自家…

numpy 平方_Numpy的终极备忘录

作者|Rashida Nasrin Sucky 编译|VK 来源|Towards Data Science Python是开源的。对于使用python的数据科学家来说,Numpy这个库是必不可少的。其他一些基本的库,如Pandas,Scipy是建立在Numpy的基础上。所以我决定做一份备忘录。这里我包括了到…

linux HZ 值_Linux的serial串口控制台

本人大多数情况都是在调试服务器大量的linux服务器,很多情况下也不没有必要专门准备KVM(keyboard, video, mouse),甚至有些机器根本就没有显示器接口。如何调试的?闲来无事,分享一下。有些人说“ 给我个Lin…

nginx文件系统大小_详解Nginx系列

1.Nginx特点Nginx是一个事件驱动架构,而非传统过程驱动架构。具有内存占用低,当并发连接大时,能够预测内存使用率。Nginx改变了传统的web服务器体系架构,提高了响应速度,起初Nginx开发的目标是实现10倍以上的性能&…

不可用于python编程开发的是_Python编程语言可做而不应做的一些事是什么_Python视频_Python视频教程_编程语言_课课家...

在 -5 ~ 256 之间的整型数值可以进行互换 当我在考虑这个能用在什么地方的时候,我有点迷茫了。撇开这点,你在此之前知道 Python 中的数字是可以改变其实际含义的吗?之所有会有这种情况,是因为 Python 解释器为 -5 ~ 256 之间的每一…

iphone7尺寸_iPhone 12 mini、12 Pro Max真机对比图赏:尺寸直观感受下

11月6日消息,等待多时,苹果iPhone 12 mini、iPhone 12 Pro Max将于今晚21点正式开启预定,11月13日发售。iPhone 12 mini是苹果尺寸最小、最轻薄的5G手机,而iPhone 12 Pro Max则是综合实力最强大,拍照最好的iPhone。赶在…

人脸识别错误代码437是什么意思_lol手游repeat ready check fails什么意思 解决攻略大全...

导读 lol手游repeat ready check fails什么意思?在日服内出现英文的错误代码,这两种语言的差异,会让国服玩家变得越来越混乱,想要解决问题,还是一如既往的得弄清楚代码内提示的内容是什么... lol手游repeat ready chec…

matlab求傅里叶级数展开式_连续时间的傅里叶级数

如果信号x(t)是周期信号,那么对于所有t,存在一个最小正数T,使得x(t)x(tT)其中T为这个周期信号的最小正周期。根据周期函数的周期性:x(t)x(tN*T)(N为整数)称为这个信号的基波频率周期信号x(t)也可以用周期复指数信号表示则因为x(t)是周期信号&…

delphi chart 曲线实时_发展学生曲线跑能力的体育游戏及运用研究

不想错过精彩的推送?戳左上角蓝字“体育教师大本营”关注并点击右上角●●●菜单栏选择“设置⭐️标”或“置顶公众号”每天早上7:00,体委伴您成长 一、曲线跑教材跑是人类在日常生活社会交往、生产劳动中的基本活动能力之一,从一定程度上反映…

拖拽的方式使用qbuttongroup_【无机纳米材料科研制图——Photoshop 0402】PS使用选框工具修改图片/图层...

此篇,我们来分享使用PS的选框修改图片。一、栅格化图形。1)同时选中两个图层。首先,回到上一篇中图片相互遮挡的状态。在上一篇中,我们通过调整图层的上下位置,避免了遮挡的情况,但有时候只通过调整图层的上…

pagerank数据集_从小白视角理解数据挖掘十大算法

关注上方“数据挖掘工程师”,选择星标,关键时间,第一时间送达!作者 | 雪山飞猪链接 | https://www.cnblogs.com/chenqionghe/p/12301905.html编辑 | 深度学习这件小事本文仅作技术交流,如有侵权,请联系删除…