(六)趣学设计模式 之 代理模式!

目录
- 一、啥是代理模式?
- 二、为什么要用代理模式?
- 三、代理模式的实现方式
- 1. 静态代理
- 2. JDK动态代理
- 3. CGLIB动态代理
- 四、三种代理的对比
- 五、代理模式的优缺点
- 六、代理模式的应用场景
- 七、总结
🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解建造者模式请看: (五)趣学设计模式 之 建造者模式!
🌟同时我的 JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?也不错
这篇文章带你详细认识一下设计模式中的代理模式
一、啥是代理模式?
代理模式,就像找了个替身 👯! 你想做一件事情,但是自己不想做,或者不能做,就找个代理人(替身)来帮你做 🤝。 代理人可以帮你处理一些额外的事情,比如权限控制 👮、日志记录 📝、缓存 📦 等等,让你更专注于核心业务 🎯!
简单来说,就是你不想直接面对某个对象,就找个代理来帮你!
- 你想做的事情比较敏感: 就像访问一些需要权限的资源 🔑,你需要先验证身份,才能访问!
- 你想做的事情比较耗时: 就像下载一个大文件 💾,你需要等待很长时间,才能完成!
- 你想做的事情比较复杂: 就像购买机票 ✈️,你需要比较不同的航班、价格和时间,才能做出选择!
二、为什么要用代理模式?
用代理模式,好处多多 👍:
- 保护目标对象: 代理可以控制对目标对象的访问,防止恶意操作 🛡️! 就像保镖保护明星 🌟,防止粉丝的疯狂行为!
- 增强目标对象的功能: 代理可以在目标对象执行前后做一些额外的事情,比如日志记录、性能监控 📈! 就像经纪人帮明星安排行程、处理事务,让明星更专注于表演!
- 延迟加载: 代理可以在需要的时候才创建目标对象,节省资源 ⏳! 就像懒加载图片 🖼️,只有当图片出现在屏幕上时,才加载图片!
- 远程代理: 代理可以代表远程对象,让你像访问本地对象一样访问远程对象 🌐! 就像 VPN,让你访问被墙的网站!
- 降低耦合度: 客户端不需要直接依赖目标对象,只需要依赖代理对象,降低了系统的耦合度 🔗!
三、代理模式的实现方式
代理模式主要分为三种:
- 静态代理: 代理类在编译时就已经确定,就像提前找好的替身演员 🎬!
- JDK动态代理: 代理类在运行时动态生成,需要实现接口,就像临时找来的替身演员 🎭!
- CGLIB动态代理: 代理类在运行时动态生成,不需要实现接口,通过继承实现,就像 AI 换脸技术 🤖!
1. 静态代理
静态代理,顾名思义,代理类是提前写好的,就像提前找好的替身演员,随时可以上场 💃!
案例:火车站卖票(经典案例 🚂)
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦 😫。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了 😊。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。
代码示例:
// 卖票接口
public interface SellTickets {void sell(); // 卖票
}// 火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票"); // 卖票}
}// 代售点
public class ProxyPoint implements SellTickets {private TrainStation station = new TrainStation(); // 持有火车站对象的引用public void sell() {System.out.println("代理点收取一些服务费用"); // 增强功能station.sell(); // 调用火车站的卖票方法}
}// 测试类
public class Client {public static void main(String[] args) {ProxyPoint pp = new ProxyPoint(); // 创建代理对象pp.sell(); // 调用代理对象的卖票方法}
}
分析:
从上面代码中可以看出,测试类直接访问的是 ProxyPoint 类对象,也就是说 ProxyPoint 作为访问对象和目标对象的中介 🤝。同时也对 sell 方法进行了增强(代理点收取一些服务费用 💰)。
输出结果:
代理点收取一些服务费用
火车站卖票
2. JDK动态代理
JDK动态代理,代理类是在运行时动态生成的,需要实现接口,就像临时找来的替身演员,需要会表演 💃!
案例:还是火车站卖票(升级版 🚂)
还是上面的火车站卖票的例子,但是这次我们使用JDK动态代理来实现 🚀!
代码示例:
// 卖票接口
public interface SellTickets {void sell(); // 卖票
}// 火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票"); // 卖票}
}// 代理工厂,用来创建代理对象
public class ProxyFactory {private TrainStation station = new TrainStation(); // 持有火车站对象的引用public SellTickets getProxyObject() {// 使用Proxy获取代理对象/*newProxyInstance()方法参数说明:ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口InvocationHandler h : 代理对象的调用处理程序*/SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),station.getClass().getInterfaces(),new InvocationHandler() {/*InvocationHandler中invoke方法参数说明:proxy : 代理对象method : 对应于在代理对象上调用的接口方法的 Method 实例args : 代理对象调用接口方法时传递的实际参数*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理点收取一些服务费用(JDK动态代理方式)"); // 增强功能// 执行真实对象Object result = method.invoke(station, args); // 调用火车站的卖票方法return result; // 返回结果}});return sellTickets; // 返回代理对象}
}// 测试类
public class Client {public static void main(String[] args) {// 获取代理对象ProxyFactory factory = new ProxyFactory();SellTickets proxyObject = factory.getProxyObject(); // 获取代理对象proxyObject.sell(); // 调用代理对象的卖票方法}
}
分析:
使用了动态代理,我们思考下面问题 🤔:
-
ProxyFactory是代理类吗? 🙅ProxyFactory不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类 💫。 -
动态代理的执行流程是什么样? ➡️
- 客户端调用代理对象的
sell()方法。 - 代理对象调用
InvocationHandler的invoke()方法。 invoke()方法中,我们可以增强功能,比如收取服务费用。invoke()方法中,通过反射调用真实对象的sell()方法。- 真实对象执行
sell()方法,返回结果。 invoke()方法将结果返回给代理对象。- 代理对象将结果返回给客户端。
- 客户端调用代理对象的
输出结果:
代理点收取一些服务费用(JDK动态代理方式)
火车站卖票
3. CGLIB动态代理
CGLIB动态代理,代理类是在运行时动态生成的,不需要实现接口,通过继承实现,就像 AI 换脸技术,直接把你的脸换成别人的脸 🎭!
案例:还是火车站卖票(终极版 🚂)
还是上面的火车站卖票的例子,但是这次我们使用CGLIB动态代理来实现 🚀! 如果没有定义 SellTickets 接口,只定义了 TrainStation (火车站类)。很显然JDK代理是无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理 😫。
代码示例:
// 火车站
public class TrainStation {public void sell() {System.out.println("火车站卖票"); // 卖票}
}// 代理工厂
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class ProxyFactory implements MethodInterceptor {private TrainStation target = new TrainStation(); // 持有火车站对象的引用public TrainStation getProxyObject() {// 创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数Enhancer enhancer = new Enhancer(); // 创建 Enhancer 对象// 设置父类的字节码对象enhancer.setSuperclass(target.getClass()); // 设置父类// 设置回调函数enhancer.setCallback(this); // 设置回调// 创建代理对象TrainStation obj = (TrainStation) enhancer.create(); // 创建代理对象return obj; // 返回代理对象}/*intercept方法参数说明:o : 代理对象method : 真实对象中的方法的Method实例args : 实际参数methodProxy :代理对象中的方法的method实例*/public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)"); // 增强功能Object result = methodProxy.invokeSuper(o, args); // 调用火车站的卖票方法return result; // 返回结果}
}// 测试类
public class Client {public static void main(String[] args) {// 创建代理工厂对象ProxyFactory factory = new ProxyFactory(); // 创建代理工厂// 获取代理对象TrainStation proxyObject = factory.getProxyObject(); // 获取代理对象proxyObject.sell(); // 调用代理对象的卖票方法}
}
分析:
CGLIB动态代理不需要接口,它是通过继承来实现的。
ProxyFactory创建了一个Enhancer对象,类似于 JDK 动态代理的Proxy类。enhancer.setSuperclass(target.getClass())设置父类为目标对象。enhancer.setCallback(this)设置回调函数为MethodInterceptor接口的实现类,也就是ProxyFactory本身。enhancer.create()创建代理对象,实际上是创建了一个目标对象的子类。- 当调用代理对象的
sell()方法时,会调用MethodInterceptor接口的intercept()方法。 - 在
intercept()方法中,我们可以增强功能,比如收取服务费用。 methodProxy.invokeSuper(o, args)调用父类(目标对象)的sell()方法。
输出结果:
代理点收取一些服务费用(CGLIB动态代理方式)
火车站卖票
四、三种代理的对比
我的文章: JDK动态代理 vs CGLIB:一场经纪人之战,谁才是你的最佳选择?讲的更好一点,可以看看
-
JDK代理 vs CGLIB代理:
特性 JDK代理 CGLIB代理 接口 必须实现接口 不需要实现接口 实现方式 通过 Proxy.newProxyInstance()动态生成代理类通过继承目标类动态生成子类 性能 相对较慢 相对较快 使用场景 目标对象实现了接口 目标对象没有实现接口 依赖 JDK自带,无需额外依赖 需要引入 CGLIB 库 -
动态代理 vs 静态代理:
特性 静态代理 动态代理 代理类 编译时确定 运行时动态生成 灵活性 较低 较高 代码量 较大 较小 维护性 较差 较好 适用场景 代理对象数量较少,代理逻辑比较固定 代理对象数量较多,代理逻辑比较灵活
五、代理模式的优缺点
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用 🛡️! 就像保镖保护明星,防止粉丝的疯狂行为!
- 代理对象可以扩展目标对象的功能 📈! 就像经纪人帮明星安排行程、处理事务,让明星更专注于表演!
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度 🔗! 就像中间商,连接买家和卖家,让双方不用直接接触!
- 符合开闭原则,可以在不修改目标对象的情况下,增加新的代理类,扩展功能 🆕!
缺点:
- 增加了系统的复杂度 😫! 需要创建多个类,代码量比较大!
- 可能会降低性能 🐌! 代理对象会增加额外的开销,可能会影响性能!
六、代理模式的应用场景
- 远程代理: 就像 VPN,让你访问被墙的网站 🌐!
- 虚拟代理: 就像懒加载图片 🖼️,只有当图片出现在屏幕上时,才加载图片!
- 保护代理: 就像访问一些需要权限的资源 🔑,你需要先验证身份,才能访问!
- 缓存代理: 就像 CDN,缓存静态资源,提高访问速度 🚀!
- 防火墙代理: 就像防火墙,保护你的电脑免受病毒攻击 🛡️!
- 事务代理: 控制数据库事务的提交和回滚 💱!
七、总结
- 代理模式就像找了个替身,帮你做一些事情! 👯
- 主要分为静态代理、JDK动态代理和CGLIB动态代理三种! 🎭
- 优点是保护目标对象、扩展功能、降低耦合度、符合开闭原则! 👍
- 缺点是增加复杂度、可能降低性能! 👎
- 适用于需要控制对目标对象的访问、增强目标对象的功能、延迟加载、远程代理等场景! 🎯
希望这篇文章能让你彻底理解代理模式! 💯 祝你学习愉快! 😄
看完请看:(七)趣学设计模式 之 适配器模式!
相关文章:
(六)趣学设计模式 之 代理模式!
目录 一、啥是代理模式?二、为什么要用代理模式?三、代理模式的实现方式1. 静态代理2. JDK动态代理3. CGLIB动态代理 四、三种代理的对比五、代理模式的优缺点六、代理模式的应用场景七、总结 🌟我的其他文章也讲解的比较有趣😁&a…...
力扣LeetCode:1656 设计有序流
题目: 有 n 个 (id, value) 对,其中 id 是 1 到 n 之间的一个整数,value 是一个字符串。不存在 id 相同的两个 (id, value) 对。 设计一个流,以 任意 顺序获取 n 个 (id, value) 对,并在多次调用时 按 id 递增的顺序…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Ads Kit(2)
1.问题描述: 应用需要获取一个唯一不变的标识生成deviceID。 当前通过OAID生成,但每次重启PC样机,获取到的OAID都会变化,无法满足唯一不变的需求。 解决方案: 需要获取一个唯一不变的标识,可以尝试使用O…...
鸿蒙开发深入浅出03(封装通用LazyForEach实现懒加载)
鸿蒙开发深入浅出03(封装通用LazyForEach实现懒加载) 1、效果展示2、ets/models/BasicDataSource.ets3、ets/models/HomeData.ets4、ets/api/home.ets5、ets/pages/Home.ets6、ets/views/Home/SwiperLayout.ets7、后端代码 1、效果展示 2、ets/models/Ba…...
DSP芯片C6678的SRIO及其中断跳转的配置
C6678SRIO读写测试门铃中断跳转测试 SRIO简述代码前言SRIO配置原始代码1.使能电源2.初始化SRIO回环修改 3.SRIO测试 Doorbell门铃中断1.初始化中断函数2.中断向量表建立3.中断向量表的链接 本博客基于创龙“678ZH产品线”的SRIO代码,部分参考于网友们的博客…...
2025asp.net全栈技术开发学习路线图
2025年技术亮点: Blazor已全面支持WebAssembly 2.0标准 .NET 8版本原生集成AI模型部署能力 Azure Kubernetes服务实现智能自动扩缩容 EF Core新增向量数据库支持特性 ASP.NET 全栈开发关键技术说明(2025年视角) 以下技术分类基于现…...
DeepSeek开源周首日:发布大模型加速核心技术可变长度高效FlashMLA 加持H800算力解码性能狂飙升至3000GB/s
FlashMLA的核心技术特性包括对BF16精度的全面支持,以及采用块大小为64的页式键值缓存(Paged KV Cache)系统,实现更精确的内存管理。在性能表现方面,基于CUDA12.6平台,FlashMLA在H800SXM5GPU上创下了显著成绩…...
01 冲突域和广播域的划分
目录 1、冲突域和广播域的划分 1.1、冲突域 1.2、广播域 1.3、对比总结 1.4、冲突域与广播域个数计算例题 2、交换机和路由器的结构 2.1、交换机的结构 2.2、路由器的结构 1、冲突域和广播域的划分 1.1、冲突域 冲突域是指网络中可能发生数据帧冲突的物理范围。当多…...
nodejs npm install、npm run dev运行的坎坷之路
1、前面的种种都不说了,好不容易运行起来oap-portal项目,运行idm-ui项目死活运行不起来,各种报错,各种安装,各种卸载nodejs,卸载nvm,重装,都不好使。 2、甚至后来运行npm install会…...
Golang 构建学习
Golang 构建学习 如何搭建Golang开发环境 1. 下载GOlang包 https://golang.google.cn/dl/ 在地址上下载Golang 2. 配置包环境 修改全局环境变量,GOPROXY,GOPATH,GOROOT GOPROXYhttps://goproxy.cn,direct GOROOT“” // go二进制文件的路…...
Android Audio实战——音频相关基础概念(附)
Android Audio 开发其实就是媒体源数字化的过程,通过将声波波形信号通过 ADC 转换成计算机支持的二进制的过程叫做音频采样 (Audio Sampling)。采样 (Sampling) 的核心是把连续的模拟信号转换成离散的数字信号。 一、声音的属性 1、响度 (Loudness) 响度是指人类可以感知到的…...
大型装备故障诊断解决方案
大型装备故障诊断解决方案 方案背景 在全球航空工业迅猛发展的背景下,我国在军用和民用飞机自主研发制造领域取得了显著成就。尤其是在国家大力支持下,国内飞机制造企业攻克了诸多关键技术难题,实现了从设计研发到生产制造再到售后保障的完整…...
反向代理模块kfj
1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求,然后将请求转发给内部网络上的服务器,将从服务器上得到的结果返回给客户端,此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说,反向代理就相当于…...
【Http和Https区别】
概念: 一、Http协议 HTTP(超文本传输协议)是一种用于传输超媒体文档(如HTML)的应用层协议,主要用于Web浏览器和服务器之间的通信。http也是客户端和服务器之间请求与响应的标准协议,客户端通常…...
【llm对话系统】如何快速开发一个支持openai接口的llm server呢
核心思路:使用轻量级 Web 框架,将 OpenAI API 请求转换为你现有推理脚本的输入格式,并将推理脚本的输出转换为 OpenAI API 的响应格式。 快速开发步骤列表: 选择合适的 Web 框架 (快速 & 简单): FastAPI: Python 最佳选择&am…...
数据库的三大范式如何理解?
数据库的三大范式是指数据库设计中用来规范化表结构的规则。其目的是减少数据冗余,提高数据一致性和完整性。三大范式分别是: 第一范式(1NF)—— 原子性 第一范式要求表中的每个字段都必须是原子的,即字段中的值不可…...
Python Seaborn库使用指南:从入门到精通
1. 引言 Seaborn 是基于 Matplotlib 的高级数据可视化库,专为统计图表设计。它提供了更简洁的 API 和更美观的默认样式,能够轻松生成复杂的统计图表。Seaborn 在数据分析、机器学习和科学计算领域中被广泛使用。 本文将详细介绍 Seaborn 的基本概念、常用功能以及高级用法,…...
Android之APP更新(通过接口更新)
文章目录 前言一、效果图二、实现步骤1.AndroidManifest权限申请2.activity实现3.有版本更新弹框UpdateappUtilDialog4.下载弹框DownloadAppUtils5.弹框背景图 总结 前言 对于做Android的朋友来说,APP更新功能再常见不过了,因为平台更新审核时间较长&am…...
JVM生产环境问题定位与解决实战(二):JConsole、VisualVM到MAT的高级应用
生产问题定位指南:几款必备的可视化工具 引言 在上一篇文章中,详细的介绍了JDK自带的一系列命令行工具,,如jps、jmap、jstat、jstack以及jcmd等,这些工具为排查和诊断Java虚拟机(JVM)问题提供…...
wsl2安装的ext4.vhdx瘦身、打包、导入
1.清理APT缓存: Ubuntu使用APT进行软件包管理,它会在安装过程中保留下载的软件包。清理这些缓存可以释放空间: sudo apt-get clean2.删除不必要的软件包: 删除不再需要的软件包和它们的依赖项: sudo apt-get autoremove3.压缩磁盘空间 ex…...
力扣3102.最小化曼哈顿距离
力扣3102.最小化曼哈顿距离 题目 题目解析及思路 题目要求返回移除一个点后的最小的最大曼哈顿距离 最大最小值的题一般直接想到二分 本题有一个简单办法就是利用切比雪夫距离 当正方形转45,即边上点**( x , y ) -> (x y , y - x)时,两点间max(…...
国标28181协议在智联视频超融合平台中的接入方法
一. 国标28181介绍 国标 28181 协议全称是《安全防范视频监控联网系统信息传输、交换、控制技术要求》,是国内视频行业最重要的国家标准,目前有三个版本: 2011 年:推出 GB/T 28181-2011 版本,为安防行业的前端设备、平…...
【学习笔记】LLM+RL
文章目录 1 合成数据与模型坍缩(model collapse),1.1 递归生成数据与模型坍缩1.2 三种错误1.3 理论直觉1.4 PPL指标 2 基于开源 LLM 实现 O1-like step by step 慢思考(slow thinking),ollama,streamlit2.1…...
Linux故障排查和性能优化面试题及参考答案
目录 如何查看 Linux 系统中的 CPU、内存、磁盘等资源使用情况? 什么是 Linux 中的负载(Load Average)?如何解读它? 如何通过 top 和 htop 命令监控系统性能? 如何使用 mpstat 命令来查看 CPU 的利用情况? 如何分析系统 CPU 瓶颈? 如何分析 CPU 瓶颈?如何优化 CP…...
【论文精读】YOLO-World:实时开放词汇目标检测
论文地址: YOLO-World: Real-Time Open-Vocabulary Object Detection 源代码:YOLO-World 摘要 YOLO系列检测器因其高效性和实用性而被广泛认可。然而,它们依赖于预定义和训练过的物体类别,这限制了其在开放场景中的适用性。为了…...
【AI时代】可视化训练模型工具LLaMA-Factory安装与使用
文章目录 安装训练使用 安装 官方地址:https://github.com/hiyouga/LLaMA-Factory 创建虚拟环境 conda create -n llama-factory conda activate llama-factory安装 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip in…...
Docker 部署 OnlyOffice 文档服务器
Docker 部署 OnlyOffice 文档服务器 前言一、准备工作二、设置变量和目录结构三、创建并运行 OnlyOffice 容器四、访问 OnlyOffice 文档服务器五、配置和管理总结 前言 OnlyOffice 是一个强大的开源文档编辑平台,支持文档、表格、演示文稿等文件格式的编辑。通过 D…...
将产品照片(form.productPhotos)转为 JSON 字符串发送给后端
文章目录 1. 前端 form.productPhotos 的当前处理a. 组件绑定b. 当前发送逻辑 2. 如何将 form.productPhotos 转为 JSON 字符串发送给后端a. 修改前端 save() 方法b. 确保 esave API 支持接收字符串 基于你提供的 identify-form.vue 代码,我将分析如何将产品照片&a…...
【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin scatter plot Venn)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载画图1画图2画图3画图4画图5画图6画图7参考介绍 【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin & scatter plot & Venn) 加载R包 library…...
kotlin 知识点一 变量和函数
在Kotlin中定义变量的方式和Java 区别很大,在Java 中如果想要定义一个变 量,需要在变量前面声明这个变量的类型,比如说int a表示a是一个整型变量,String b表 示b是一个字符串变量。而Kotlin中定义一个变量,只允许在变量…...
