设计模式之(8)——代理模式

定义:为某个对象提供一个代理,以达到对这个对象的访问控制,代理类和委托类有共同的父类或者父接口,这样可以在使用委托类的地方都可以使用代理对象来替换(这符合程序设计中的” 里氏替换原则“),代理类负责请求的预处理、过滤等初步处理之后,再将请求分派给委托类进行处理,代理类当然也可以在委托类执行完毕之后做一些其它工作;

代理模式根据代理类的生成时间不同可以静态代理和动态代理。

静态代理:是由程序员创建或工具生成代理类的源码,在编译期就已经确定了委托类和代理类,在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就已经确定了。

动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成的 ,所以不存在代理类的字节码文件,代理类和委托类的关系在程序运行时动态确定。

说了这么多那么代理有什么优点呢?

说起了一大堆,总的来说主要有两大点:1、可以对客户端隐藏委托类的实现;2、可以实现客户端和委托类之间的解耦,在不修改委托类的情况下做一些其他的处理,当然核心业务还得调用委托类的方法处理;

日常生活中代理的场景很常见,比如说我们有一套房子需要出售,但是我们没有时间自己天天带看房,那么我们就可以把这套房子挂委托给房产中介,中介就可以帮我们筛选潜在客户,带客户看房,确定买家之后,中介就联系我们和买家签订合同,买家付款、双方完成过户、房屋物业水电燃气交接事宜等,在这个过程中卖家就是委托类,而中介就是代理类;

其实在Java中也有很多场景需要使用代理,如RPC的远程调用,我们就是通过代理类去实现的,还有Spring中的AOP切面也是为切面生成了代理类;

下面我们先讲讲静态代理的实现:

1、定义接口和接口的实现(委托类);

2、定义代理类(定义接口的代理对象);

3、将接口的实例注入到代理对象中,然后通过代理对象去调用委托类的实现;

静态代理的示例代码如下:

通过以上代码我们也不难发现静态代理的缺点也很明显:假设系统中有N个委托类需要代理,那么可能就需要N个代理类,这就容易造成系统的类爆炸,再者假如委托类中的方法很多,那么也可能在代理类中存在大量的重复代码,所以我们可以看出静态代理的可复用性不高。那么我们如何解决上面这个问题呢?答案就是动态代理。

动态代理分为两种一种是基于接口的jdk的动态代理,一种是基于继承的cglib的动态代理,我们先来说说jdk的动态代理。

一个JAVA类在JVM中的生命周期分为这几个过程:加载-》验证->准备-》解析-》初始化-》使用-》卸载,而其中的加载阶段主要完成以下三件事情:

1、通过一个类的全限定名来获取定义此类的二进制流;

2、将这个字节流所代表的静态数据结构转换为方法区的运行时数据结构;

3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口;

而我们要说的动态代理,主要就发生在第一个阶段, 这个阶段类的二进制字节流的来源可以有很多, 比如 zip 包、网络、运行时计算生成、其它文件生成 (JSP)、数据库获取。其中运行时计算生成就是我们所说的动态代理技术,在 Proxy 类中, 就是运用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 * $Proxy 的代理类的二进制字节流。所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进 JVM 中。实际计算的情况会很复杂,我们借助一些诸如 JDK 动态代理实现;

jdk的动态代理是在程序运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类生成代理对象的实例;

下面是jdk动态代理测试的demo:

在以上的测试代码中,我们调用Proxy类的newProxyInstance()方法来获取一个代理对象的实例,这个实例实现了我们指定的接口,并且会把方法调用分发到我们指定的调用处理器MyInvocationHandler中,调用invoke()方法,我们在invoke()方法中调用委托类的对应方法,并添加上自己的处理逻辑;

jdk的动态代理最大的特点是 代理类和委托类实现共同的接口,jdk的动态代理内部其实是通过反射机制来实现的,已知一个对象,在运行的时候动态调用它的方法,并且在调用的时候还可以家上一些自己的逻辑在里面;

那假如没有结构我们该如何实现动态代理呢?这时候我们的cglib动态代理就横空出世啦。

cglib的动态代理是通过一个第三方框架来实现的,所以我们在使用的时候应该引入对应的jar包,例如:

其原理大致是:对指定的委托类生成一个子类并重写其中的业务方法来实现的;

cglib的测试代码如下:

cglib动态代理的创建过程可以总结为以下几个步骤:

1、查找目标类定义的所有非final修饰的public类型的方法;

2、将符合条件的方法定义转换成字节码;

3、将字节码转换成相应的代理的class对象;

4、实现MethodInterceptor接口,用来处理对所有代理方法的拦截;

jdk动态代理和cglib动态代理的比较:

jdk的动态代理:基于反射来实现,委托类必须实现了接口才能创建代理类,代码实现简单,简化了开发和维护,jdk原生支持,反射速度较慢;

cglib的动态代理:基于ASM机制,通过字节码技术,通过生成委托类的子类,采用方法拦截的技术来拦截所有父类方法的调用,织入横切逻辑,完成代理,并且无需实现接口,达到代理类的无侵入,只关心业务类即可,并且是直接操作字节码生成的,速度上有一定的优势,但是无法对final修饰的方法进行代理,spring全家桶中的很多功能是通过这种方式来实现的;

注意事项:

1、和适配器模式相比,适配器模式重点在于改变所考虑对象的接口,而代理不能改变所代理对象的接口;

2、和装饰设计模式相比,装饰设计模式重点在于强调类功能的增强,而代理模式的重点在于对象的访问控制;

Original: https://www.cnblogs.com/wha6239/p/16650461.html
Author: 一只烤鸭朝北走
Title: 设计模式之(8)——代理模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/612890/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 锁定文件失败 打不开磁盘“D:Windows7Windows7 64 位.vmdk”或它所依赖的某个快照磁盘。 模块“Disk”启动失败。

    Windows7虚拟机非正常关闭,再次打开有时候会出现”锁定文件失败,打不开磁盘……”的错误提示解决办法:打开虚拟机所在路径删除.v…

    数据库 2023年6月14日
    087
  • 面试连环炮系列(二十八):数据库读写分离的目的是什么?

    1. 数据库读写分离的目的是什么? 通常,商业系统读得更多,写得更少。读写分离将读写操作分散到不同的节点,可以小幅提升写性能,大幅提升读性能。通常在数据库中采用一主多从的方式,主数…

    数据库 2023年5月24日
    095
  • StoneDB(石原子科技)受邀参与《开源数据库服务商服务能力分级要求》标准第一次讨论会

    2022年8月9日下午,StoneDB数据库主体研发单位石原子科技与华为、openGauss开源社区、云和恩墨、甲骨文等组织受邀参与《开源数据库服务商服务能力分级要求》标准第一次讨…

    数据库 2023年5月24日
    074
  • 翻译官方文档或文章小姿势

    翻译官方文档或文章小姿势 首先抛出一个观点: 不太建议初学者翻译官方文档或文章 这个观点针对的是”初学者”,如果是老鸟并且业余时间很多,请绕行 :-) 第一…

    数据库 2023年6月9日
    088
  • [LeetCode]剑指 Offer 17. 打印从1到最大的n位数

    输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n = 1输出: [1,2,3…

    数据库 2023年6月9日
    092
  • Git

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    数据库 2023年6月16日
    095
  • 2022蓝帽杯初赛wp(取证)

    战果 取证全解 misc出了1个 解其他题就像在坐牢 有那么一点思路,但不是完全有 手机取证_1 解压并打开阅读器,搜索627604C2-C586-48C1-AA16-FF33C3…

    数据库 2023年6月11日
    0101
  • 设计模式之建造者模式

    一、建造者模式:如果创建某个对象要经过多个组件组装才能完成,我们可以设计一个充当建造者角色的类和一个充当指挥者的类,通过指挥者控制建造者按步骤组装需要创建的对象,这样客户端就只依赖…

    数据库 2023年6月14日
    083
  • 做数据时代的加油站,ShardingSphere 为易车数据库架构演进提供新动力

    Apache ShardingSphere 前段时间应邀来到易车北京总部,PMC Chair 张亮与易车的技术同学在数据加解密、扩容、迁移、上云等话题展开了深度交流与探讨。 作为中…

    数据库 2023年6月16日
    0243
  • 2018年最新JAVA面试题总结之基础(1)

    转自于:https://zhuanlan.zhihu.com/p/39322967 1、JAVA中能创建volatile数组吗?volatile能使得一个非原子操作变成原子操作吗?…

    数据库 2023年6月16日
    092
  • AspNetCoreapi 使用 Docker + Centos 7部署

    好久没有更新文章了,前段时间写了一系列的文章放到桌面了,想着修修改改,后来系统中勒索病毒了还被公司网络安全的抓到是我电脑,后来装系统文章给装丢了。然后好长一段时间没有写了。 今天记…

    数据库 2023年6月11日
    0100
  • Java 线程创建与常用方法

    进程与线程 进程 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、…

    数据库 2023年6月16日
    069
  • 23种设计模式之策略模式

    文章目录 概览 策略模式的优缺点 策略模式的应用场景 策略模式的结构与实现 * 模式的结构 模式的实现 策略模式的扩展 总结 ; 概览 策略模式定义了一系列算法,并将每个算法封装起…

    数据库 2023年6月6日
    0121
  • MySQL 中的锁机制

    技术是为了解决问题而生的,锁被用来实现隔离性,保证并发事务的正确性。 两段锁 数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁) 加锁阶段:在加锁阶段…

    数据库 2023年6月11日
    0102
  • 第十章 对象的生命周期

    1.什么是生命周期 对象创建 存活 销毁的完整的过程 2.为什么学习对象的生命周期 在以前通过new创建对象,调用对象,则该对象存活,直到被JVM的垃圾回收机制回收 现在由Spri…

    数据库 2023年6月14日
    092
  • MySQL事务与锁

    在关系型数据库内,事务是由一个SQL或一组SQL语句组成的逻辑处理单元。也就是说事务就相当于一个盛放SQL的容器,事务中的SQL要么全部执行成功,要么所有已经修改的操作都回滚到原来…

    数据库 2023年5月24日
    0105
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球