设计模式之(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)

大家都在看

  • 20 年老程序员告诉你的 20 条编码原则

    我从 1999 年就开始了编程生涯,到今年已经有 20 多年了。我先是从 Basic 开始,很快转到了 Pascal 和 C 语言,然后又学习了面向对象编程语言 Delphi 和 …

    数据库 2023年6月14日
    084
  • Shell第四章《正则表达式》

    1.1、名词解释 正则表达式(regular expression, RE)是一种字符模式,用于在查找过程中匹配指定的字符。在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/…

    数据库 2023年6月14日
    079
  • mysql8.x docker 远程访问配置

    环境情况 mysql 8.x 是通过 docker 方式部署的,启动的 docker-compose.yml 如下: version: "3.2" servic…

    数据库 2023年5月24日
    060
  • 数据库性能优化八大方案,你知道几个

    前言 毫不夸张的说咱们后端工程师,无论在哪家公司,呆在哪个团队,做哪个系统,遇到的第一个让人头疼的问题绝对是数据库性能问题。 如果我们有一套成熟的方法论,能让大家快速、准确的去选择…

    数据库 2023年6月14日
    071
  • Python–序列化与反序列化

    序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态(存在内存中)写入到临时或持久性存储区(硬盘)。以后,可以通过从存储区中读取或反序列化对象的…

    数据库 2023年6月9日
    093
  • mysql中文乱码记录

    0.中文乱码 1.查看mysql中表结构 show create table log_data charset为utf8,代码端:通过gorm修改 在创建表时候修改( 这里在连接数…

    数据库 2023年5月24日
    081
  • Java基础一—面向对象三大特性

    写在最前 本系列为个人对BAT大厂面试题与全栈知识体系结合的简化梳理及本人在日常学习中一些知识的整理(包括但不限于书本、他人博客、微信公众号等渠道),仅为个人总结学习与整理知识框架…

    数据库 2023年6月6日
    069
  • [spring]spring的bean自动装配机制

    是spring满足bean依赖的一种方式 spring会在上下文中自动寻找,并自动给bean装配属性 spring的装配方式: (1)手动装配 在people类中依赖了cat和do…

    数据库 2023年6月16日
    068
  • 设计 | ClickHouse 分布式表实现数据同步

    作者:吴帆 青云数据库团队成员主要负责维护 MySQL 及 ClickHouse 产品开发,擅长故障分析,性能优化。 在多副本分布式 ClickHouse 集群中,通常需要使用 D…

    数据库 2023年5月24日
    093
  • 通过Python收集汇聚MySQL 表信息

    一.需求 统计收集各个实例上table的信息,主要是表的记录数及大小。 收集的范围是cmdb中所有的数据库实例。 二.公共基础文件说明 1.配置文件 配置文为db_servers_…

    数据库 2023年6月16日
    0104
  • 实现随机验证码

    Java实现随机验证码的生成 随机验证码: 法一:普通方法 核心逻辑: 1.定义一个String类型的变量存储验证码字符。 2.定义一个for循环,循环n次(n为验证码的所需要字符…

    数据库 2023年6月16日
    0106
  • FastDFS安装和简介详细总结

    1、fastDFS简介 1 FastDFS是用c语言编写的一款开源的分布式文件系统。 2 FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用…

    数据库 2023年6月14日
    0105
  • pg 锁表

    select * from pg_catalog.pg_stat_activity where usename =’gis_bd_app’ and wait…

    数据库 2023年6月6日
    094
  • NO.5 MySQL-笔记

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

    数据库 2023年6月14日
    055
  • 工具 | 常用 MySQL 内核 Debug 技巧

    作者:柯煜昌 顾问软件工程师目前从事 RadonDB MySQL 容器化研发,华中科技大学研究生毕业,有多年的数据库内核开发经验。 掌握 MySQL 内核源码的阅读和调试能力,不仅…

    数据库 2023年5月24日
    0105
  • 华为云操作记录——JavaWeb 环境搭建

    华为云操作记录 创建用户 新建用户 sudo adduser weirwei 添加免密 root 权限 sudo vim /etc/sudoers 添加 root 权限 sudo …

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