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

news2025/2/26 3:34:23

在这里插入图片描述

目录

    • 一、啥是代理模式?
    • 二、为什么要用代理模式?
    • 三、代理模式的实现方式
      • 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 不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类 💫。

  • 动态代理的执行流程是什么样? ➡️

    1. 客户端调用代理对象的 sell() 方法。
    2. 代理对象调用 InvocationHandlerinvoke() 方法。
    3. invoke() 方法中,我们可以增强功能,比如收取服务费用。
    4. invoke() 方法中,通过反射调用真实对象的 sell() 方法。
    5. 真实对象执行 sell() 方法,返回结果。
    6. invoke() 方法将结果返回给代理对象。
    7. 代理对象将结果返回给客户端。

输出结果:

代理点收取一些服务费用(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动态代理三种! 🎭
  • 优点是保护目标对象、扩展功能、降低耦合度、符合开闭原则! 👍
  • 缺点是增加复杂度、可能降低性能! 👎
  • 适用于需要控制对目标对象的访问、增强目标对象的功能、延迟加载、远程代理等场景! 🎯

希望这篇文章能让你彻底理解代理模式! 💯 祝你学习愉快! 😄
看完请看:(七)趣学设计模式 之 适配器模式!

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

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

相关文章

力扣LeetCode:1656 设计有序流

题目&#xff1a; 有 n 个 (id, value) 对&#xff0c;其中 id 是 1 到 n 之间的一个整数&#xff0c;value 是一个字符串。不存在 id 相同的两个 (id, value) 对。 设计一个流&#xff0c;以 任意 顺序获取 n 个 (id, value) 对&#xff0c;并在多次调用时 按 id 递增的顺序…

鸿蒙开发深入浅出03(封装通用LazyForEach实现懒加载)

鸿蒙开发深入浅出03&#xff08;封装通用LazyForEach实现懒加载&#xff09; 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代码&#xff0c;部分参考于网友们的博客&#xf…

2025asp.net全栈技术开发学习路线图

2025年技术亮点‌&#xff1a; Blazor已全面支持WebAssembly 2.0标准 .NET 8版本原生集成AI模型部署能力 Azure Kubernetes服务实现智能自动扩缩容 EF Core新增向量数据库支持特性 ‌ASP.NET 全栈开发关键技术说明&#xff08;2025年视角&#xff09;‌ 以下技术分类基于现…

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、前面的种种都不说了&#xff0c;好不容易运行起来oap-portal项目&#xff0c;运行idm-ui项目死活运行不起来&#xff0c;各种报错&#xff0c;各种安装&#xff0c;各种卸载nodejs&#xff0c;卸载nvm&#xff0c;重装&#xff0c;都不好使。 2、甚至后来运行npm install会…

大型装备故障诊断解决方案

大型装备故障诊断解决方案 方案背景 在全球航空工业迅猛发展的背景下&#xff0c;我国在军用和民用飞机自主研发制造领域取得了显著成就。尤其是在国家大力支持下&#xff0c;国内飞机制造企业攻克了诸多关键技术难题&#xff0c;实现了从设计研发到生产制造再到售后保障的完整…

反向代理模块kfj

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…

Python Seaborn库使用指南:从入门到精通

1. 引言 Seaborn 是基于 Matplotlib 的高级数据可视化库,专为统计图表设计。它提供了更简洁的 API 和更美观的默认样式,能够轻松生成复杂的统计图表。Seaborn 在数据分析、机器学习和科学计算领域中被广泛使用。 本文将详细介绍 Seaborn 的基本概念、常用功能以及高级用法,…

Android之APP更新(通过接口更新)

文章目录 前言一、效果图二、实现步骤1.AndroidManifest权限申请2.activity实现3.有版本更新弹框UpdateappUtilDialog4.下载弹框DownloadAppUtils5.弹框背景图 总结 前言 对于做Android的朋友来说&#xff0c;APP更新功能再常见不过了&#xff0c;因为平台更新审核时间较长&am…

JVM生产环境问题定位与解决实战(二):JConsole、VisualVM到MAT的高级应用

生产问题定位指南&#xff1a;几款必备的可视化工具 引言 在上一篇文章中&#xff0c;详细的介绍了JDK自带的一系列命令行工具&#xff0c;&#xff0c;如jps、jmap、jstat、jstack以及jcmd等&#xff0c;这些工具为排查和诊断Java虚拟机&#xff08;JVM&#xff09;问题提供…

力扣3102.最小化曼哈顿距离

力扣3102.最小化曼哈顿距离 题目 题目解析及思路 题目要求返回移除一个点后的最小的最大曼哈顿距离 最大最小值的题一般直接想到二分 本题有一个简单办法就是利用切比雪夫距离 当正方形转45&#xff0c;即边上点**( x , y ) -> (x y , y - x)时&#xff0c;两点间max(…

国标28181协议在智联视频超融合平台中的接入方法

一. 国标28181介绍 国标 28181 协议全称是《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是国内视频行业最重要的国家标准&#xff0c;目前有三个版本&#xff1a; 2011 年&#xff1a;推出 GB/T 28181-2011 版本&#xff0c;为安防行业的前端设备、平…

【学习笔记】LLM+RL

文章目录 1 合成数据与模型坍缩&#xff08;model collapse&#xff09;,1.1 递归生成数据与模型坍缩1.2 三种错误1.3 理论直觉1.4 PPL指标 2 基于开源 LLM 实现 O1-like step by step 慢思考&#xff08;slow thinking&#xff09;&#xff0c;ollama&#xff0c;streamlit2.1…

【论文精读】YOLO-World:实时开放词汇目标检测

论文地址&#xff1a; YOLO-World: Real-Time Open-Vocabulary Object Detection 源代码&#xff1a;YOLO-World 摘要 YOLO系列检测器因其高效性和实用性而被广泛认可。然而&#xff0c;它们依赖于预定义和训练过的物体类别&#xff0c;这限制了其在开放场景中的适用性。为了…

【AI时代】可视化训练模型工具LLaMA-Factory安装与使用

文章目录 安装训练使用 安装 官方地址&#xff1a;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…

将产品照片(form.productPhotos)转为 JSON 字符串发送给后端

文章目录 1. 前端 form.productPhotos 的当前处理a. 组件绑定b. 当前发送逻辑 2. 如何将 form.productPhotos 转为 JSON 字符串发送给后端a. 修改前端 save() 方法b. 确保 esave API 支持接收字符串 基于你提供的 identify-form.vue 代码&#xff0c;我将分析如何将产品照片&a…

【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin scatter plot Venn)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载画图1画图2画图3画图4画图5画图6画图7参考介绍 【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin & scatter plot & Venn) 加载R包 library…

kotlin 知识点一 变量和函数

在Kotlin中定义变量的方式和Java 区别很大&#xff0c;在Java 中如果想要定义一个变 量&#xff0c;需要在变量前面声明这个变量的类型&#xff0c;比如说int a表示a是一个整型变量&#xff0c;String b表 示b是一个字符串变量。而Kotlin中定义一个变量&#xff0c;只允许在变量…

solidity之Foundry安装配置(一)

一门面向合约的高级编程语言&#xff0c;主要用来编写以太坊只能合约。 Solidity受C语言&#xff0c;Python和js影响&#xff0c;但为编译成为以太坊虚拟机字节码在EVM上执行&#xff0c;很多特性和限制都和EVM相关。 Solidity 是静态类型语言&#xff0c;支持继承、库、自定义…