当前位置: 首页 > news >正文

设计模式之代理模式

前言

说到代理你首选会想到什么:

不方便自己做的事,要不要找个代理?比如说:美国人的代理人战争

自己搞不定或者不擅长的事,要不要找个代理?比如说:代汽车上牌、房产中介、黄牛等;

没错,这就是代理。当然这篇文章主要是来盘一盘设计模式中的代理模式。

什么是代理模式

代理模式是一种设计模式,它为其他对象提供一种代理以控制对该对象的访问。即用户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式给某一个对象提供一个代理对象,并由代理对象控制原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。通过代理类,起到了中介隔离作用,代理类除了是客户类和委托类的中介隔离作用之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类,而不需要在修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转达给委托类,以及时候对返回结果的处理等。代理类本身不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又比如找女朋友、找保姆、找工作等都可以通过找中介完成

代理模式的特点

  1. 代理模式能够隐藏真实对象的实现细节,使客户端无需知晓真实对象的工作方式和结构。
  2. 通过代理类来间接访问真实类,可以在不修改真实类的情况下对其进行扩展、优化或添加安全措施。
  3. 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。

代理模式的核心角色

代理模式有四个核心角色,UML类图与详细介绍如下:

  1. 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理对象可以替代真实对象。它通常是一个接口或抽象类。
  2. 真实主题(Real Subject):实现了抽象主题接口,是真正执行业务逻辑的类。
  3. 代理(Proxy):实现了抽象主题接口,持有对真实主题的引用。在需要时,它将请求传递给真实主题,并可以在请求前后执行额外的逻辑。
  4. 客户端:通过代理对象来访问真实对象。

代理模式如何实现

根据代理的创建时期,代理模式分为静态代理和动态代理。

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
  • 动态代理:在程序运行时,运用反射机制动态创建而成

静态代理

水浒传相信很多人都看过,这里就以西门庆通过王婆与潘金莲私会的情节举个例子,在这个情节中,王婆实际上都是充当一个代理人的角色,自己本身并不执行约会这件事,实际执行约会这件事的人是金莲;当然了,每次西门庆想与金莲玩耍一番,不会直接上金莲家的,而是到王婆家中,给王婆交上几两银子,然后王婆再把金莲叫来,一番玩耍后,王婆还会再假模假样的客气一番:“官人再来”之类的话。

如果画一个UML类图来表示这件事的话,如下:

代码示例:

1、定义抽象的约会类(抽象主题);

2、 定义金莲(真实的主题);

3、定义王婆(代理);

4、定义西门庆(客户端);

/*** 约会*/
public interface YueHui {/*** 玩耍*/void play();
}
/*** 金莲*/
public class PanJinLian implements YueHui{@Overridepublic void play() {System.out.println("金莲:玩得很开心呢");}
}
/*** 王婆*/
public class WangPo implements YueHui {private  PanJinLian panJinLian;public WangPo(PanJinLian panJinLian) {this.panJinLian = panJinLian;}@Overridepublic void play() {this.beforePlay();panJinLian.play();this.afterPlay();}private void beforePlay(){System.out.println("王婆:交五两银子");}private void afterPlay(){System.out.println("王婆:欢迎下次再来");}
}
/*** 西门庆*/
public class XiMenQing {public static void main(String[] args) {PanJinLian panJinLian = new PanJinLian();WangPo wangPo = new WangPo(panJinLian);wangPo.play();}
}

动态代理

动态代理:是在程序运行时,运用反射机制动态创建而成,从实现上又分为两类:

  • 基于接口的动态代理(如InvocationHandler),即JDK动态代理
  • 基于类的动态代理,使用CGLIB直接生成代理类;

这里主要分享一下JDK动态代理这种方式,另一种方式有兴趣的小伙伴可以自行再拓展一下。

还是上面的例子,用Java的动态代理再来实现一下;其实也很简单,上面的静态代理对象WangPo是在代码编译前已经实现好的,这里的动态代理对象panJinLianProxy则是通过Proxy类使用Java反射技术在程序运行时动态生成的;

public class WangPoHandler implements InvocationHandler {private PanJinLian panJinLian;public WangPoHandler(PanJinLian panJinLian) {this.panJinLian = panJinLian;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {this.beforePlay();Object result = method.invoke(panJinLian, args);this.afterPlay();return result;}private void beforePlay() {System.out.println("王婆:交五两银子");}private void afterPlay() {System.out.println("王婆:欢迎下次再来");}
}
public class XiMenQing2 {public static void main(String[] args) {PanJinLian panJinLian = new PanJinLian();WangPoHandler wangPoHandler = new WangPoHandler(panJinLian);YueHui panJinLianProxy = (YueHui) Proxy.newProxyInstance(WangPoHandler.class.getClassLoader(), PanJinLian.class.getInterfaces(), wangPoHandler);panJinLianProxy.play();}
}

代理模式在Spring中的应用

在Spring框架中,代理模式应用场景如下:

  1. AOP(面向切面编程):Spring中的AOP实现主要基于代理模式。Spring会自动扫描容器中的组件,对于需要进行AOP处理的组件,Spring会使用动态代理创建代理对象,并将其放入容器中。当业务方法被调用时,代理对象会捕获到调用,并在调用前后执行增强逻辑。
  2. RPC远程调用:在RPC远程调用中,代理模式可以用于处理网络通信和数据传输。通过使用代理模式,客户端可以像调用本地方法一样调用远程对象的方法,代理对象会负责将请求发送到远程服务器并等待响应。
  3. 注解对象获取:Spring框架的注解对象获取,比如通过@Autowired注解获取bean对象,实际上也是使用了代理模式。Spring会在启动时扫描所有的类,对于有@Autowired注解的属性或方法,Spring会自动为其生成代理对象,并注入相应的依赖。
  4. 日志框架、全局性异常处理、事务处理、权限管理、MyBatis的Mapper层接口等:在这些场景中,代理模式可以增强目标对象的功能,扩展其行为,或者在目标对象前后添加额外的操作,比如日志记录、事务处理等。

但是需要注意的是,Spring中的代理模式主要有两种实现方式:基于JDK的动态代理和基于CGLib的动态代理。其中,基于JDK的动态代理主要依赖于Java反射机制,通过为每个需要代理的类生成一个子类来实现代理;而基于CGLib的动态代理则通过在运行时创建子类来实现代理。

总结

优点

  1. 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  2. 代理对象可以扩展目标对象的功能;
  3. 代理模式能将客户端与目标对象分离,职责清晰,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

缺点

  1. 代理模式会造成系统设计中类的数量增加
  2. 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  3. 增加了系统的复杂度;

相关文章:

  • 【RabbitMQ 实战】12 镜像队列
  • 跟我学C++中级篇——右值引用和万能引用
  • Positive Technologies 在迪拜宣布与地区网络安全解决方案提供商开展合作
  • 环形链表(C++解法)
  • 京东销量(销额)数据分析:2023年9月京东奶粉行业品牌销售排行榜
  • Linux高性能服务器编程——ch9笔记
  • 空间统计学:快速理解反距离加权法(IDW)
  • 智能水厂运行与调控3D模拟仿真在线展示提高整个系统的协同效应
  • Docker GitLab-Runner安装
  • 【API篇】八、Flink窗口函数
  • CSS读书笔记
  • 解决 /bin/bash^M: bad interpreter: No such file or directory
  • 软考 系统架构设计师系列知识点之设计模式(9)
  • Spring定时任务+webSocket实现定时给指定用户发送消息
  • python和Springboot如何交互?
  • ES6指北【2】—— 箭头函数
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Hibernate【inverse和cascade属性】知识要点
  • Linux下的乱码问题
  • Puppeteer:浏览器控制器
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • React-flux杂记
  • Spark学习笔记之相关记录
  • 近期前端发展计划
  • 聊一聊前端的监控
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 前端自动化解决方案
  • 正则与JS中的正则
  • 中文输入法与React文本输入框的问题与解决方案
  • 数据库巡检项
  • 我们雇佣了一只大猴子...
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (多级缓存)缓存同步
  • (附源码)ssm码农论坛 毕业设计 231126
  • (十一)c52学习之旅-动态数码管
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • **CI中自动类加载的用法总结
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .net 使用ajax控件后如何调用前端脚本
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .net项目IIS、VS 附加进程调试
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • .so文件(linux系统)
  • [ 云计算 | AWS ] AI 编程助手新势力 Amazon CodeWhisperer:优势功能及实用技巧
  • []FET-430SIM508 研究日志 11.3.31
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [14]内置对象
  • [16/N]论得趣
  • [20160807][系统设计的三次迭代]
  • [BJDCTF 2020]easy_md5