news 2026/4/16 18:59:48

设计模式:代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式:代理模式

目录

一、核心角色

二、代理模式的分类

关键补充:动态代理(Spring AOP 的底层原理)

三、核心作用(解决的问题)

四、代理模式 vs 装饰者模式

五、在无人售货柜项目中的典型应用


代理模式是结构型设计模式的一种,核心思想是通过一个 “代理对象” 来控制对 “真实对象” 的访问,代理对象可以在真实对象的调用前后添加额外逻辑,同时对外隐藏真实对象的实现细节。

简单来说,代理模式就像生活中的中介:你租房不用直接找房东,而是通过中介(代理)完成看房、签约、缴费等流程,中介还会帮你筛选房源、处理售后问题。

一、核心角色

代理模式包含 4 个核心角色,以无人售货柜项目IoT设备通信场景为例:

  1. 抽象主题(Subject)定义真实对象和代理对象的共同接口,确保代理对象可以替代真实对象。

    java

    运行

    // 设备通信接口(抽象主题) public interface DeviceCommunicator { // 发送指令给售货柜硬件 void sendCommand(String deviceId, String command); // 接收设备上报数据 String receiveData(String deviceId); }
  2. 真实主题(Real Subject)实现抽象主题接口,是业务逻辑的实际执行者(真正和硬件交互的类)。

    java

    运行

    // 真实设备通信类(真实主题) public class RealDeviceCommunicator implements DeviceCommunicator { @Override public void sendCommand(String deviceId, String command) { // 实际的硬件通信逻辑:如通过MQTT/串口发送指令 System.out.println("向设备[" + deviceId + "]发送指令:" + command); } @Override public String receiveData(String deviceId) { // 模拟从硬件接收传感器数据(如库存、温度) return "设备[" + deviceId + "]上报数据:库存剩余50件,温度25℃"; } }
  3. 代理对象(Proxy)持有真实主题的引用,实现抽象主题接口,在调用真实对象方法时添加额外逻辑(如日志、权限校验、缓存)。

    java

    运行

    // 设备通信代理类(代理对象) public class DeviceCommunicatorProxy implements DeviceCommunicator { // 持有真实对象的引用 private final DeviceCommunicator realCommunicator; public DeviceCommunicatorProxy() { this.realCommunicator = new RealDeviceCommunicator(); } @Override public void sendCommand(String deviceId, String command) { // 前置增强:权限校验 + 日志记录 if (!checkPermission(deviceId)) { throw new RuntimeException("无权限操作设备:" + deviceId); } System.out.println("【代理】发送指令前日志:设备ID=" + deviceId + ",指令=" + command); // 调用真实对象的方法 realCommunicator.sendCommand(deviceId, command); // 后置增强:记录指令发送结果 System.out.println("【代理】发送指令成功"); } @Override public String receiveData(String deviceId) { // 前置增强:缓存校验(避免重复请求硬件) String cacheData = getCacheData(deviceId); if (cacheData != null) { return "【缓存数据】" + cacheData; } // 调用真实对象获取数据 String realData = realCommunicator.receiveData(deviceId); // 后置增强:缓存数据 setCacheData(deviceId, realData); return "【实时数据】" + realData; } // 模拟权限校验 private boolean checkPermission(String deviceId) { return deviceId.startsWith("CABINET_"); // 仅允许操作售货柜设备 } // 模拟本地缓存 private final Map<String, String> dataCache = new HashMap<>(); private String getCacheData(String deviceId) { return dataCache.get(deviceId); } private void setCacheData(String deviceId, String data) { dataCache.put(deviceId, data); } }
  4. 客户端(Client)只和代理对象交互,无需知道真实对象的存在。

    java

    运行

    public class Client { public static void main(String[] args) { // 使用代理对象,而非直接使用RealDeviceCommunicator DeviceCommunicator proxy = new DeviceCommunicatorProxy(); // 发送指令(自动触发权限校验和日志) proxy.sendCommand("CABINET_001", "OPEN_DOOR"); // 接收数据(首次获取实时数据,后续获取缓存数据) System.out.println(proxy.receiveData("CABINET_001")); System.out.println(proxy.receiveData("CABINET_001")); } }

二、代理模式的分类

根据代理对象的创建时机和方式,可以分为 3 种常见类型,对应不同的业务场景:

类型核心特点适用场景无人售货柜项目案例
静态代理代理类在编译期就已确定,和真实类一一对应业务逻辑固定、简单的场景上述的DeviceCommunicatorProxy,为设备通信添加固定的权限和缓存逻辑
动态代理代理类在运行期动态生成,无需手动编写代理类多个类需要相同的代理逻辑(如统一日志、事务)为所有微服务接口添加统一的多租户数据隔离拦截:通过 JDK 动态代理,在接口调用前自动拼接租户 ID 作为查询条件
远程代理代理对象和真实对象不在同一进程,通过网络通信分布式系统、跨服务调用售货柜云平台通过代理调用远程的支付结算服务:代理对象负责封装 HTTP 请求、处理网络异常、重试机制

关键补充:动态代理(Spring AOP 的底层原理)

在无人售货柜的微服务架构中,动态代理的应用远比静态代理广泛。例如,通过 Spring AOP 实现全局的接口日志、事务管理、多租户拦截,其底层就是动态代理。

多租户数据隔离为例,通过 JDK 动态代理实现统一拦截:

java

运行

// 租户上下文(存储当前请求的租户ID) public class TenantContext { private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>(); public static void setTenantId(String tenantId) { TENANT_ID.set(tenantId); } public static String getTenantId() { return TENANT_ID.get(); } } // 动态代理处理器 public class TenantProxyHandler implements InvocationHandler { private final Object target; // 真实对象(如Service层Bean) public TenantProxyHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强:获取租户ID,修改查询参数(如给Mapper的参数添加tenantId) String tenantId = TenantContext.getTenantId(); if (tenantId != null && args != null) { for (int i = 0; i < args.length; i++) { if (args[i] instanceof BaseQuery) { ((BaseQuery) args[i]).setTenantId(tenantId); } } } // 执行真实方法 return method.invoke(target, args); } } // 客户端使用动态代理 public class DynamicProxyClient { public static void main(String[] args) { // 真实对象:如商品服务的Service GoodsService realService = new GoodsServiceImpl(); // 生成动态代理对象 GoodsService proxyService = (GoodsService) Proxy.newProxyInstance( realService.getClass().getClassLoader(), realService.getClass().getInterfaces(), new TenantProxyHandler(realService) ); // 设置当前租户ID TenantContext.setTenantId("TENANT_001"); // 调用代理方法,自动添加租户ID条件 proxyService.queryGoods(new GoodsQuery()); } }

三、核心作用(解决的问题)

  1. 功能增强在不修改真实对象代码的前提下,添加额外逻辑(如日志、缓存、权限、事务),符合开闭原则
  2. 隐藏细节对客户端隐藏真实对象的实现细节(如硬件通信的协议、远程服务的地址),降低客户端的使用成本。
  3. 控制访问限制对真实对象的直接访问(如权限校验、限流、熔断),保护核心业务逻辑。

四、代理模式 vs 装饰者模式

很多开发者会混淆这两种模式,核心区别如下:

维度代理模式装饰者模式
核心目的控制对真实对象的访问给真实对象添加功能
关系代理对象持有真实对象的引用,是 “控制者”装饰者和真实对象实现同一接口,是 “增强者”
场景权限校验、远程调用、缓存功能扩展(如给 IO 流添加缓冲、加密功能)

五、在无人售货柜项目中的典型应用

  1. IoT 设备通信层:用静态代理添加指令日志、设备权限校验、数据缓存
  2. 微服务接口层:用动态代理(Spring AOP)实现多租户数据隔离、接口限流、全局异常处理
  3. 远程服务调用:用远程代理(如 Feign 客户端)封装跨服务调用的网络请求、重试、降级逻辑
  4. 支付结算模块:用代理模式封装不同支付渠道(微信、支付宝)的调用细节,对上层提供统一接口。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 3:47:11

GNU Coreutils正则表达式工具:Linux文本处理终极指南

正则表达式是文本处理的强大工具&#xff0c;而GNU coreutils项目中的grep、sed和awk更是将这种能力发挥到了极致&#xff01;无论你是系统管理员、开发者还是数据分析师&#xff0c;掌握这些Linux命令行工具都能让你的工作效率提升数倍。本文将为你提供完整的正则表达式学习指…

作者头像 李华
网站建设 2026/4/15 21:28:04

从朴素RAG到高级智能问答:一篇值得收藏的RAG技术演进全指南

1. 摘要&#xff1a;从朴素RAG到高阶智能问答的范式演进 检索增强生成&#xff08;RAG&#xff09;作为一种行之有效的技术范式&#xff0c;通过引入外部知识库&#xff0c;有效缓解了大型语言模型&#xff08;LLM&#xff09;固有的幻觉&#xff08;Hallucination&#xff09;…

作者头像 李华
网站建设 2026/4/15 22:30:26

大模型岗位全景图:从预训练到Prompt,小白也能看懂,建议收藏学习

文章将大模型岗位分为五个梯队&#xff1a;第一梯队是预训练和Infra工程师&#xff0c;技术壁垒最高&#xff1b;第二梯队是模型优化、后训练和多模态专家&#xff1b;第三梯队是应用开发工程师&#xff0c;需求最大&#xff1b;第四梯队包括数据工程、风控和评估等支撑岗位&am…

作者头像 李华
网站建设 2026/4/16 10:41:47

监测应用版本更新

监测应用版本更新一、版本更新判断核心二、项目入口文件src/App.vue中引入封装好的检查版本更新组件&#xff1a;三、组件内容&#xff1a;1. 引入依赖2. 定义变量3. checkRefresh 函数4. start 函数5. 生命周期钩子四、组件完整示例&#xff1a;文件内容的变化反映版本更新&am…

作者头像 李华
网站建设 2026/4/16 9:00:51

PyTorch与TensorFlow共存可能吗?看v2.7镜像的多框架支持

PyTorch与TensorFlow共存可能吗&#xff1f;看v2.7镜像的多框架支持 在深度学习工程师的日常工作中&#xff0c;你是否曾遇到这样的场景&#xff1a;刚跑通一个PyTorch写的模型&#xff0c;转头就要复现一篇TensorFlow实现的论文&#xff1b;团队里有人坚持用torch.nn&#xff…

作者头像 李华