在软件开发中,我们经常需要控制对某些对象的访问——可能是为了延迟加载、添加额外功能或保护敏感资源。这正是代理模式大显身手的地方。作为结构型设计模式的重要成员,代理模式在众多知名框架和系统中扮演着关键角色。本文将全面剖析代理模式的方方面面,带你领略这一模式的精妙之处。

一、代理模式概述

1.1 什么是代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。简单来说,代理就是一个"替身",它代表另一个对象(即真实对象)执行某些操作,同时可以在访问真实对象前后添加自己的逻辑。

1.2 代理模式的核心思想

代理模式的核心在于"控制访问",它遵循了面向对象设计原则中的"单一职责原则"和"开闭原则":

  • 单一职责:真实对象只需关注核心业务逻辑,而访问控制、日志记录等职责交给代理

  • 开闭原则:可以在不修改真实对象代码的情况下,通过代理扩展功能

1.3 生活中的代理类比

现实生活中代理的例子比比皆是:

  • 房屋中介:代理房东处理租房事宜

  • 明星经纪人:代理明星安排演出和商业活动

  • 信用卡:代理银行账户进行支付

这些代理的共同特点是:它们都代表另一个实体执行操作,同时可以添加自己的处理逻辑(如中介收取佣金、经纪人筛选邀约等)。

二、代理模式的结构与实现

2.1 UML类图

2.2 核心角色

  1. Subject(抽象主题)

    • 定义真实主题和代理主题的共同接口

    • 可以是接口或抽象类

  2. RealSubject(真实主题)

    • 实现真实业务逻辑的类

    • 是被代理的对象

  3. Proxy(代理)

    • 包含对真实主题的引用

    • 实现与真实主题相同的接口

    • 可以控制对真实主题的访问

2.3 Java实现示例

// 抽象主题
interface Database {void query(String sql);
}// 真实主题
class RealDatabase implements Database {@Overridepublic void query(String sql) {System.out.println("执行查询: " + sql);// 实际数据库操作...}
}// 代理
class DatabaseProxy implements Database {private RealDatabase realDatabase;private List<String> blacklist = Arrays.asList("DELETE", "DROP", "TRUNCATE");@Overridepublic void query(String sql) {// 安全检查if (containsBlacklistedKeywords(sql)) {throw new RuntimeException("查询包含危险操作");}// 日志记录System.out.println("[" + LocalDateTime.now() + "] 执行查询: " + sql);// 延迟初始化if (realDatabase == null) {realDatabase = new RealDatabase();}// 执行实际查询realDatabase.query(sql);// 后置处理System.out.println("查询完成");}private boolean containsBlacklistedKeywords(String sql) {return blacklist.stream().anyMatch(sql::contains);}
}// 客户端代码
public class Client {public static void main(String[] args) {Database database = new DatabaseProxy();database.query("SELECT * FROM users");  // 正常执行database.query("DROP TABLE users");     // 抛出异常}
}

这个示例展示了一个数据库查询代理,它实现了:

  1. 安全检查(保护代理)

  2. 日志记录(智能引用代理)

  3. 延迟初始化(虚拟代理)

三、代理模式的类型

3.1 远程代理(Remote Proxy)

特点:为位于不同地址空间的对象提供本地代表

应用场景

  • RPC(远程过程调用)

  • Web服务客户端

  • 分布式系统中的存根(Stub)

示例

// 远程服务接口
interface WeatherService {String getWeather(String city);
}// 本地代理
class WeatherServiceProxy implements WeatherService {@Overridepublic String getWeather(String city) {// 通过网络调用远程服务return callRemoteWeatherService(city);}private String callRemoteWeatherService(String city) {// 实际网络通信逻辑...return "Sunny";}
}

3.2 虚拟代理(Virtual Proxy)

特点:根据需要创建开销很大的对象

应用场景

  • 大图加载

  • 复杂对象初始化

  • 资源密集型操作

示例

class HighResolutionImage implements Image {public HighResolutionImage(String path) {loadImage(path); // 耗时操作}private void loadImage(String path) {// 加载大图...}
}class ImageProxy implements Image {private String path;private HighResolutionImage realImage;public ImageProxy(String path) {this.path = path;}@Overridepublic void show() {if (realImage == null) {realImage = new HighResolutionImage(path); // 延迟加载}realImage.show();}
}

3.3 保护代理(Protection Proxy)

特点:控制对原始对象的访问权限

应用场景

  • 权限控制

  • 敏感操作保护

  • 访问限制

示例

interface BankAccount {void withdraw(double amount);double getBalance();
}class RealBankAccount implements BankAccount {private double balance;@Overridepublic void withdraw(double amount) {balance -= amount;}@Overridepublic double getBalance() {return balance;}
}class BankAccountProxy implements BankAccount {private RealBankAccount account;private User user;public BankAccountProxy(User user) {this.user = user;this.account = new RealBankAccount();}@Overridepublic void withdraw(double amount) {if (user.hasPermission("WITHDRAW")) {account.withdraw(amount);} else {throw new SecurityException("无取款权限");}}@Overridepublic double getBalance() {if (user.hasPermission("VIEW_BALANCE")) {return account.getBalance();} else {throw new SecurityException("无查看余额权限");}}
}

3.4 智能引用代理(Smart Reference Proxy)

特点:在访问对象时执行附加操作

应用场景

  • 引用计数

  • 对象池管理

  • 缓存机制

示例

class ExpensiveObject {void process() {System.out.println("处理中...");}
}class SmartProxy {private ExpensiveObject realObject;private int accessCount = 0;public void process() {if (realObject == null) {realObject = new ExpensiveObject();}accessCount++;System.out.println("访问次数: " + accessCount);realObject.process();if (accessCount >= 5) {System.out.println("重置对象...");realObject = null;accessCount = 0;}}
}

四、代理模式的深入应用

4.1 Spring框架中的代理

Spring框架广泛使用代理模式,主要体现在:

  1. AOP(面向切面编程)

    • Spring AOP使用JDK动态代理或CGLIB代理实现

    • 为业务组件添加事务管理、日志记录等横切关注点

  2. 事务管理

    • @Transactional注解背后的代理机制

    • 在方法调用前后管理事务边界

  3. @Configuration类

    • 配置类的代理确保@Bean方法单例性

示例

@Service
public class UserService {@Transactionalpublic void createUser(User user) {// 数据库操作}
}// Spring在运行时创建代理类
class UserServiceProxy extends UserService {private UserService target;private PlatformTransactionManager txManager;@Overridepublic void createUser(User user) {TransactionStatus status = txManager.getTransaction(new DefaultTransactionDefinition());try {target.createUser(user);txManager.commit(status);} catch (Exception e) {txManager.rollback(status);throw e;}}
}

4.2 MyBatis中的代理

MyBatis使用JDK动态代理实现Mapper接口:

public interface UserMapper {@Select("SELECT * FROM users WHERE id = #{id}")User getUserById(int id);
}// MyBatis在运行时生成代理实现
class MapperProxy implements InvocationHandler {private SqlSession sqlSession;@Overridepublic Object invoke(Object proxy, Method method, Object[] args) {// 解析注解中的SQLString sql = method.getAnnotation(Select.class).value();// 执行SQL并返回结果return sqlSession.selectOne(sql, args[0]);}
}

4.3 RPC框架中的代理

远程方法调用(RPC)框架如Dubbo、gRPC都使用代理模式:

// 服务接口
public interface OrderService {Order getOrder(long id);
}// 客户端代理
class OrderServiceProxy implements OrderService {private String serviceUrl;@Overridepublic Order getOrder(long id) {// 序列化参数byte[] request = serialize(id);// 网络调用byte[] response = sendRequest(serviceUrl, request);// 反序列化结果return deserialize(response);}
}

五、代理模式的优缺点

5.1 优点

  1. 职责分离:代理对象处理非功能性需求(如安全、日志),真实对象专注业务逻辑

  2. 开闭原则:无需修改真实对象即可扩展功能

  3. 访问控制:代理可以控制对真实对象的访问

  4. 性能优化:虚拟代理可以实现延迟加载,提高系统响应速度

5.2 缺点

  1. 复杂度增加:引入代理层会增加系统复杂度

  2. 性能开销:代理调用会带来额外的处理时间

  3. 间接性:可能使调试变得困难,因为调用栈更深

六、代理模式与相关模式的比较

6.1 代理模式 vs 装饰器模式

对比维度代理模式装饰器模式
目的控制访问增强功能
关系代理知道被代理对象的生命周期装饰器与被装饰对象独立
关注点访问机制(如延迟加载、权限控制)添加新行为

6.2 代理模式 vs 适配器模式

对比维度代理模式适配器模式
接口保持相同接口转换不同接口
目的控制访问解决接口不兼容问题
使用时机设计阶段规划后期集成时使用

七、实际应用建议

  1. 何时使用代理模式

    • 需要延迟初始化(虚拟代理)

    • 需要控制资源访问(保护代理)

    • 需要添加横切关注点(AOP)

    • 需要远程调用(远程代理)

  2. 实现选择

    • 静态代理:代理类在编译时确定,适合简单场景

    • 动态代理:运行时生成代理类,更灵活(JDK动态代理、CGLIB)

  3. 性能考虑

    • 对于频繁调用的方法,注意代理带来的性能开销

    • 考虑使用轻量级代理或直接访问

  4. 设计原则

    • 遵循"最少知识原则",代理不应暴露过多真实对象细节

    • 保持代理接口简洁,避免成为"上帝对象"

结语

代理模式作为设计模式家族中的重要成员,其应用范围从简单的对象访问控制到复杂的框架实现无处不在。理解并掌握代理模式,不仅能帮助我们设计出更加灵活、安全的系统,还能深入理解众多流行框架的内部工作机制。无论是日常开发中的权限控制、日志记录,还是分布式系统中的远程调用,代理模式都展现出其强大的适应性和生命力。希望本文能为你打开代理模式的大门,助你在软件设计之路上更进一步。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VSCode - VSCode 快速跳转标签页

VSCode 快速跳转标签页 1、标签页列表快速跳转 通过快捷键 Ctrl Tab 即可快速跳转标签页 # 操作方式先按住 Ctrl 键&#xff0c;再按 Tab 键&#xff0c;此时&#xff0c;即可打开标签页列表&#xff08;保持 Ctrl 键一直按住&#xff09;然后&#xff0c;再按 Tab 键&#xf…

深入理解设计模式:享元模式(Flyweight Pattern)

在软件开发中&#xff0c;我们经常会遇到需要创建大量相似对象的情况。如果每个对象都独立存储所有数据&#xff0c;将会消耗大量内存资源&#xff0c;导致系统性能下降。享元模式&#xff08;Flyweight Pattern&#xff09;正是为解决这一问题而生的经典设计模式。本文将深入探…

网络大提速,RDMA,IB,iWrap

本章第一节介绍的存储设备方面的创新解决了CPU访问存储设备的性能问题。但在实际的业务当中,数据的传输除了在节点内部的CPU与存储设备间外,节点之间也存在数据传输的需求。本节我们就介绍在网络传输方面是如何提速的。 在介绍新的网络技术之前,我们看看传统网络是如何传输…

【C++】红黑树,“红“与“黑”的较量

各位大佬好&#xff0c;我是落羽&#xff01;一个坚持不断学习进步的大学生。 如果您觉得我的文章有所帮助&#xff0c;欢迎多多互三分享交流&#xff0c;一起学习进步&#xff01; 也欢迎关注我的blog主页: 落羽的落羽 一、红黑树的概念与规则 红黑树是一种更加特殊的平衡二…

【愚公系列】《MIoT.VC》001-认识、安装 MIoT.VC 软件

💎【行业认证权威头衔】 ✔ 华为云天团核心成员:特约编辑/云享专家/开发者专家/产品云测专家 ✔ 开发者社区全满贯:CSDN博客&商业化双料专家/阿里云签约作者/腾讯云内容共创官/掘金&亚马逊&51CTO顶级博主 ✔ 技术生态共建先锋:横跨鸿蒙、云计算、AI等前沿领域…

git:tag标签远程管理

git tag v1&#xff1a;在当前所在分支创建标签v1git tag -a v2 -m release version&#xff1a;创建一个带有附注的标签git tag -d v2&#xff1a;删除本地标签git tag&#xff1a;查看标签git push origin 标签1 标签2……&#xff1a;把多个标签推送到远程git push origin -…

力扣 hot100 Day49

105. 从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 //抄的 class Solution { private:unordered_map<int, i…

jvm-sandbox-repeater 录制和回放

https://github.com/alibaba/jvm-sandbox-repeater/blob/master/docs/user-guide-cn.md 快速录制自己应用 step0 安装sandbox和插件到应用服务器 curl -s https://github.com/alibaba/jvm-sandbox-repeater/releases/download/v1.0.0/install-repeater.sh | sh step1 修改repe…

【C++底层剖析】++a vs a++:到底谁是左值,谁是右值?

在 C 编程中&#xff0c;我们经常使用 a 和 a 来实现自增操作。乍一看它们只是“先加还是后加”的语法糖&#xff0c;但你真的理解它们的底层机制、返回值类型和左值右值属性吗&#xff1f;1. a 和 a 的基础区别表达式名称语义返回值类型左值 / 右值a前置自增先将 a 加 1&#…

【世纪龙科技】汽车故障诊断与排除仿真教学软件让课堂更高效安全

随着汽车产业向智能化、电动化快速转型&#xff0c;职业院校汽修专业的教学模式正面临全新挑战。传统实车实训存在成本高、风险大、场景单一等问题&#xff0c;而行业对人才的要求却越来越高——既需要扎实的理论基础&#xff0c;又必须具备熟练的故障诊断能力。如何在保证安全…

网络基础9:按流负载均衡实验(等价路由)

实验eNS拓扑图&#xff1a;1. 网络拓扑与 IP 配置AR5&#xff1a;GE0/0/0: 192.168.1.1/24&#xff08;连接 AR6&#xff09;GE0/0/1: 192.168.3.1/24&#xff08;连接 AR8&#xff09;Loopback0: 1.1.1.1/32&#xff08;源地址&#xff09;AR6&#xff1a;GE0/0/0: 192.168.1.…

4G模块 A7680发送中文短信到手机

命令说明 基础AT指令 ATi显示产品的标志信息 ATCIMI查询IMSI ATCICCID从SIM卡读取ICCID ATCGSN查询产品序列号 ATCPIN查询卡状态 ATCSQ查询信号强度 ATCGATT查询当前PS域状态 ATCREG查询GPRS注册状态 ATCEREG查询4G注册状态 ATCGPADDR查询PDP地址 ATCMGF选择短信格式 ATCMGS发…

深度学习-线性神经网络

文章目录线性回归基本概念随机梯度下降矢量化加速正态分布和平方损失极大似然估计线性回归实现从0开始**torch.no_grad()的两种用途****为什么需要 l.sum().backward()&#xff1f;**调用现成库softmax回归图像数据集从0开始实现softmax利用框架API实现课程学习自李牧老师B站的…

【王树森推荐系统】推荐系统涨指标的方法04:多样性

涨指标的方法有哪些&#xff1f; 改进召回模型&#xff0c;添加新的召回模型改进粗排和精排模型提升召回&#xff0c;粗排&#xff0c;精排的多样性特殊对待新用户吗&#xff0c;低活用户等特殊人群利用关注&#xff0c;转发&#xff0c;评论这三种交互行为 排序的多样性 精排多…

1. Spring AI概述

一、前言 Spring AI 是由 Spring 团队推出的开源项目&#xff0c;旨在为 Java 开发者提供简洁、一致的 Spring 风格开发体验&#xff0c;用于构建基于生成式人工智能&#xff08;GenAI&#xff09;和大型语言模型&#xff08;LLM&#xff09;的应用程序。它通过标准化抽象层简…

[每日随题10] DP - 重链剖分 - 状压DP

整体概述 难度&#xff1a;1600 →\rightarrow→ 2200 →\rightarrow→ 2600 P6005 [USACO20JAN] Time is Mooney G 标签&#xff1a;DP 前置知识&#xff1a;链式前向星 难度&#xff1a;绿 1600 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 样例输…

【Ubuntu22.04】repo安装方法

背景 repo是Google开发的用于基于git管理Android版本库的一个工具&#xff0c;管理多个Git仓库的工具&#xff0c;它可以帮助您在一个代码库中管理多个Git仓库的代码。其在鸿蒙操作系统中大量使用。下面我们就介绍repo在wsl中的安装部署。 安装方法 使用中国科技大学资源 脚本i…

Vue3的definePros和defineEmits

在 Vue 3 中&#xff0c;defineProps 和 defineEmits 是组合式 API 中用于定义组件的 props 和 事件 的方法&#xff0c;提供了一种更简洁和明确的方式来管理组件的输入和输出。它们属于 Composition API 的一部分&#xff0c;在 Vue 2 中通常使用 props 和 $emit 来实现。1. d…

【华为机试】122. 买卖股票的最佳时机 II

文章目录122. 买卖股票的最佳时机 II描述示例 1示例 2示例 3提示解题思路核心观察关键洞察算法实现方法1&#xff1a;贪心算法&#xff08;推荐&#xff09;方法2&#xff1a;动态规划方法3&#xff1a;动态规划&#xff08;空间优化&#xff09;方法4&#xff1a;波峰波谷法算…

Spring MVC @RequestParam注解全解析

RequestParam 注解详解 RequestParam 是 Spring MVC 中最常用的注解之一&#xff0c;用于从 HTTP 请求中提取查询参数&#xff08;Query String&#xff09;或表单数据。它主要处理 application/x-www-form-urlencoded 类型的请求&#xff08;如 GET 请求或 POST 表单提交&…