做啊网站/今日头条(官方版本)
1. 概念
- 代理模式(Proxy Pattern)是程序设计中的一种结构型设计模式。它为一个对象提供一个代理对象,并由代理对象控制对该对象的访问。
2. 原理结构图
- 抽象角色(Subject):这是一个接口或抽象类,它声明了代理对象和目标对象共同的接口,这样在任何使用目标对象的地方都可以使用代理对象。代理对象和目标对象对外具有一致的方法。
- 真实角色(Real Subject):也叫被代理角色,它实现了抽象角色,定义了真实对象在关注的行为中执行的操作。
- 代理角色(Proxy):也叫委托类,它同样实现了抽象角色,并持有真实角色的实例,可以在调用真实角色前后添加一些操作,也可以不调用真实角色而直接返回结果。
3. 代码示例
3.1 示例1(静态代理)
- 这个案例涉及到一个银行系统,其中有一个 Account 接口,一个实现该接口的 SavingsAccount 类,以及一个代理类 AccountProxy 来代表 SavingsAccount 对象,并添加额外的日志记录功能。
// 定义 Account 接口
interface Account { void deposit(double amount); double withdraw(double amount); double getBalance();
}// 实现 Account 接口的 SavingsAccount 类
class SavingsAccount implements Account { private double balance; public SavingsAccount(double initialBalance) { this.balance = initialBalance; } @Override public void deposit(double amount) { balance += amount; } @Override public double withdraw(double amount) { if (amount <= balance) { balance -= amount; return amount; } else { System.out.println("Insufficient balance!"); return 0; } } @Override public double getBalance() { return balance; }
}// 接着,我们创建静态代理类 AccountProxy,它实现 Account 接口并持有一个Account类型的引用(这里是SavingsAccount对象)
class AccountProxy implements Account { private Account account; public AccountProxy(Account account) { this.account = account; } @Override public void deposit(double amount) { log("Depositing " + amount); account.deposit(amount); log("Deposited successfully."); } @Override public double withdraw(double amount) { log("Withdrawing " + amount); double withdrawnAmount = account.withdraw(amount); if (withdrawnAmount > 0) { log("Withdrawn successfully."); } return withdrawnAmount; } @Override public double getBalance() { return account.getBalance(); } private void log(String message) { System.out.println("[LOG] " + message); }
}// 在客户端代码中使用 AccountProxy 来代理 SavingsAccount 对象
public class BankClient {public static void main(String[] args) {// 创建SavingsAccount对象Account savingsAccount = new SavingsAccount(1000.0);// 使用AccountProxy代理SavingsAccount对象Account proxyAccount = new AccountProxy(savingsAccount);// 存款proxyAccount.deposit(500.0);// 取款double withdrawn = proxyAccount.withdraw(300.0);System.out.println("withdrawn: " + withdrawn);// 查询余额double balance = proxyAccount.getBalance();System.out.println("Current balance: " + balance);}
}
- 输出
[LOG] Depositing 500.0
[LOG] Deposited successfully.
[LOG] Withdrawing 300.0
[LOG] Withdrawn successfully.
withdrawn: 300.0
Current balance: 1200.0
- 在这个例子中, AccountProxy 类代理了 SavingsAccount 对象,并在每次调用方法时添加了日志记录功能。这种静态代理模式使得可以在不修改原始 SavingsAccount 类的情况下,为其添加额外的功能。但是,静态代理模式的一个缺点是,对于每个需要代理的类,都需要手动创建一个代理类,这可能导致代码冗余和难以维护。在更复杂的场景中,可能会考虑使用动态代理或AOP(面向切面编程)等更高级的技术来减少代理类的编写工作。
3.2 示例2(动态代理)
- JDK动态代理模式在复杂场景下的一个典型应用是AOP(面向切面编程)。在这个场景下,我们可能会想要在不修改业务逻辑代码的情况下,为方法调用添加额外的行为,如日志记录、事务管理、安全检查等。下面是一个使用JDK动态代理模式的案例,模拟一个具有日志记录和性能监控功能的业务处理系统。
interface BusinessService {void executeBusinessLogic();
}class BusinessServiceImpl implements BusinessService {@Overridepublic void executeBusinessLogic() {System.out.println("Executing business logic...");// 模拟业务逻辑执行时间try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Business logic executed.");}
}class LoggingDynamicHandler {private Object target;public LoggingDynamicHandler(Object target) {this.target = target;}public Object getNewProxyInstance() {// 使用Proxy类的静态方法newProxyInstance来动态创建代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法调用前记录日志System.out.println("[LOG] " + method.getName() + " is called.");long startTime = System.currentTimeMillis();// 调用目标对象的方法Object result = method.invoke(target, args);// 方法调用后记录日志和性能信息long endTime = System.currentTimeMillis();long elapsedTime = endTime - startTime;System.out.println("[LOG] " + method.getName() + " completed in " + elapsedTime + " ms.");return result;}});}
}public class ProxyPatternDemo {public static void main(String[] args) {// 创建目标对象(需要被代理的对象)BusinessService businessService = new BusinessServiceImpl();// 创建LoggingDynamicHandler对象,并传入目标对象LoggingDynamicHandler handler = new LoggingDynamicHandler(businessService);// 使用LoggingDynamicHandler类的getNewProxyInstance方法获取动态代理对象BusinessService proxyBusinessService = (BusinessService) handler.getNewProxyInstance();// 通过代理对象调用方法proxyBusinessService.executeBusinessLogic();}
}
- 将看到如下输出:
[LOG] executeBusinessLogic is called.
Executing business logic...
Business logic executed.
[LOG] executeBusinessLogic completed in 1013 ms.
- 在这个例子中,当调用proxyBusinessService.executeBusinessLogic()时,实际上会调用InvocationHandler的invoke方法,并在其中记录日志和计算方法执行的时间。通过这种方式,我们可以在不修改BusinessServiceImpl代码的情况下,为其添加日志记录和性能监控功能。
- JDK动态代理模式的优点在于它简单易用,只需要目标对象实现接口,就可以为其创建代理对象。然而,它也有局限性,即只能为接口创建代理对象,不能为类创建代理对象。如果需要为类创建代理对象,可以考虑使用CGLIB等第三方库。
3.3 示例3(Cglib代理)
添加依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version>
</dependency>
- 下面是一个CGLIB代理模式的示例,假设有一个 UserService 类,该类没有实现任何接口,想要使用CGLIB来创建其代理对象,并在代理对象中添加一些额外的逻辑(例如日志记录)。
class UserService {public void addUser(String username, String password) {System.out.println("添加用户: " + username + " 密码: " + password);}
}class CglibProxyInterceptor implements MethodInterceptor {private final Object target;public CglibProxyInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在方法执行前添加逻辑beforeMethod(method);// 调用目标方法Object result = proxy.invokeSuper(obj, args);// 在方法执行后添加逻辑afterMethod(method);return result;}private void beforeMethod(Method method) {System.out.println("开始执行方法: " + method.getName());// 这里可以添加更多逻辑,比如日志记录、权限检查等}private void afterMethod(Method method) {System.out.println("结束执行方法: " + method.getName());// 这里可以添加更多逻辑,比如异常处理、资源清理等}
}class CglibProxyFactory {public static <T> T createProxy(Class<T> clazz, Object target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(new CglibProxyInterceptor(target));return (T) enhancer.create();}
}public class ProxyCglibDemo {public static void main(String[] args) {// 创建一个目标对象UserService userService = new UserService();// 使用CGLIB代理工厂创建代理对象UserService proxyUserService = CglibProxyFactory.createProxy(UserService.class, userService);// 调用代理对象的方法proxyUserService.addUser("Alice", "password123");}
}
- 将看到如下输出:
开始执行方法: addUser
添加用户: Alice 密码: password123
结束执行方法: addUser
- 在上面的示例中,UserService 类没有实现任何接口,但可以使用 CglibProxyFactory 为其创建代理对象。当调用 proxyUserService.addUser() 方法时,实际上会触发 CglibProxyInterceptor 中的 intercept 方法,可以在这个方法中添加自定义的逻辑。
- 这个示例展示了如何为任意类创建CGLIB代理对象,并添加通用代理逻辑。在实际应用中,可以根据需要扩展CglibProxyInterceptor类,添加更多的自定义逻辑。
4. 优缺点
- 主要作用
- 提供对目标对象的间接访问,以实现控制、增强或简化原有对象的功能。
- 优点
- 降低耦合度:代理模式通过引入一个中间层,即代理对象,来实现对目标对象的访问控制,这样可以减少系统各个部分之间的直接依赖,从而提高系统的扩展性和维护性。
- 保护目标对象:代理可以对目标对象进行封装,防止外部对目标对象的直接访问,从而起到保护的作用。
- 功能增强:代理可以在不改变目标对象的基础上,为目标对象添加新的功能,比如日志记录、权限检查等。
- 缺点
- 性能损耗:由于每次调用目标对象的方法都需要先经过代理对象,因此可能会带来一定的性能损耗。
- 复杂性增加:引入代理模式会增加代码的复杂性,需要额外编写代理对象的代码,并考虑代理对象与目标对象之间的交互。
- 透明度问题:对于不熟悉代理模式的开发者来说,可能会觉得代码难以理解和维护,因为代理对象的存在增加了代码的间接性。
5. 应用场景
5.1 主要包括以下几个方面
- 远程代理:用于为远程对象提供本地代表。隐藏网络连接和通讯细节,让调用者就像访问本地对象一样访问远程服务。
- 安全代理:用于控制对敏感对象的访问。可以在代理中加入访问控制逻辑,以确保只有满足特定条件的请求才能访问目标对象。
- 智能引用代理:用于控制对象的引用计数,实现对象的缓存和再利用。常用于缓存框架和对象池中。
- 日志代理:用于在不改变原有代码的情况下,为目标对象的方法增加日志记录功能。可以记录方法调用的时间、参数、返回值等信息。
- 事务代理:用于控制复杂对象的所有事务操作,确保所有操作要么全部成功,要么全部失败。
5.2 实际应用
- 在客户端-服务器架构中,远程代理可以代表一个远程对象,处理网络通信细节,使得客户端可以像与本地对象交互一样与远程对象交互。
- 在访问控制系统中,安全代理用于控制对敏感资源的访问,例如确保用户在访问文件系统或数据库前已经通过身份验证和授权检查。
- 在业务层和服务层之间添加一个代理层,用于记录方法调用的详细信息,如参数、返回值和执行时间,以便于性能监控和故障排查。
- 在企业应用中,事务代理可以包装业务操作,确保所有操作在一个事务上下文中完成,要么全部成功提交,要么在出现错误时回滚。
- …
6. JDK中的使用
- AOP(面向切面编程)
- Spring AOP 是 Spring 框架中的一个核心功能,它允许开发者定义跨越多个点的横切关注点,如安全、事务、缓存等。AOP 在运行时通过动态代理将这些横切关注点织入到目标对象的方法调用前后,从而实现对这些方法的增强。
- Spring AOP 支持两种类型的动态代理:基于接口的 JDK 动态代理和基于继承的 CGLib 动态代理。JDK 动态代理要求目标类实现一个或多个接口,而 CGLib 动态代理则不要求目标类有接口,它通过生成目标类的子类来实现代理。
- MyBatis
- 拦截器(Interceptor)机制:MyBatis允许开发者自定义拦截器来拦截对Mapper接口方法的调用。这种机制也是基于代理模式实现的,拦截器可以在方法调用前后添加自定义的逻辑,比如修改参数、处理结果或者进行日志记录等。
- Mapper接口的动态代理:在MyBatis中,当我们定义了一个Mapper接口和一个对应的XML配置文件后,我们并没有实现这个接口,但仍然可以调用它的方法来执行数据库操作。这是因为MyBatis使用动态代理来实例化这些Mapper接口。当应用程序尝试获取一个Mapper接口的实例时,MyBatis实际上返回的是一个动态生成的代理对象。这个代理对象内部持有一个指向Mapper接口的引用,并将接口方法的调用转发到这个内部引用上,进而执行相应的SQL语句。
7. 注意事项
- 明确代理目的:代理应明确增强、控制或保护目标对象的目的。
- 确保代理逻辑正确:代理类中的代理逻辑应准确无误,避免引入新的问题。
- 保持目标对象不变:代理模式不应修改目标对象的代码,仅通过代理对象进行交互。
- 考虑性能影响:代理可能会引入一定的性能开销,需权衡利弊。
- 注意线程安全:若代理对象在多线程环境下使用,需确保线程安全。
- 妥善处理异常:代理过程中可能出现的异常应得到妥善处理。
- 选择合适的代理类型:根据具体需求选择静态代理、动态代理或虚拟代理等合适的代理类型。
8. 外观模式 VS 代理模式
模式 | 目的 | 模式架构主要角色 | 应用场景 |
---|---|---|---|
外观模式 | 简化接口,减少系统的复杂性 | 外观角色和子系统角色 | 复杂的系统且包含多个子系统,简化系统的接口 |
代理模式 | 控制对被代理类的访问 | 代理角色和目标角色 | 为其他对象提供一个代理以控制对这个对象的访问 |
装饰者模式 | 为对象动态添加行为或责任,而不改变其接口 | 抽象组件和多个具体装饰者 | 适用于需要为对象添加新功能而不改变其接口的场景 |
相关文章:

设计模式-代理模式(Proxy)
1. 概念 代理模式(Proxy Pattern)是程序设计中的一种结构型设计模式。它为一个对象提供一个代理对象,并由代理对象控制对该对象的访问。 2. 原理结构图 抽象角色(Subject):这是一个接口或抽象类࿰…...

中伟视界:智慧矿山智能化预警平台功能详解
矿山智能预警平台是一种高度集成化的安全监控系统,它能够提供实时的监控和报警功能,帮助企业和机构有效预防和响应潜在的安全威胁。以下是矿山智能预警平台的一些关键特性介绍: 报警短视频生成: 平台能够在检测到报警时自动生成短…...

如何在PPT中获得网页般的互动效果
如何在PPT中获得网页般的互动效果 效果可以看视频 PPT中插入网页有互动效果 当然了,获得网页般的互动效果,最简单的方法就是在 PPT 中插入网页呀。 那么如何插入呢? 接下来为你讲解如何获得(此方法在 PowerPoint中行得通&#…...

HTML段落标签、换行标签、文本格式化标签与水平线标签
目录 HTML段落标签 HTML换行标签 HTML格式化标签 加粗标签 倾斜标签 删除线标签 下划线标签 HTML水平线标签 HTML段落标签 在网页中,要把文字有条理地显示出来,就需要将这些文字分段显示。在 HTML 标签中,<p>标签用于定义段落…...

NVIC简介
NVIC(Nested Vectored Interrupt Controller)是ARM处理器中用于中断管理的一个重要硬件模块。它负责处理来自多个中断源的中断请求,并根据中断的优先级来安排处理器执行相应的中断服务例程(ISR)。NVIC是ARM Cortex-M系…...

LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】
LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】 题目描述:解题思路一:解题思路二:0解题思路三:0 题目描述: 给出了一个由 n 个节点组成的网络,用 n n 个邻接矩阵图…...

【linux】yum 和 vim
yum 和 vim 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 查看软件包1.3 如何安装软件1.4 如何卸载软件1.5 关于 rzsz 2. Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 vim命令模式命令集2.4 vim底行模式命令集2.5 vim操作总结补充:vim下批量化注释…...

excel试题转word格式
序号试题选项答案 格式如上。输出后在做些适当调整就可以。 import pandas as pd from docx import Document from docx.shared import Inches# 读取Excel文件 df pd.read_excel(r"你的excel.xlsx")# 创建一个新的Word文档 doc Document()# 添加标题 doc.add_headi…...

C语言学习笔记之指针(二)
指针基础知识:C语言学习笔记之指针(一)-CSDN博客 目录 字符指针 代码分析 指针数组 数组指针 函数指针 代码分析(出自《C陷阱和缺陷》) 函数指针数组 指向函数指针数组的指针 回调函数 qsort() 字符指针 一…...

在Debian 12系统上安装Docker
Docker 在 Debian 12 上的安装 安装验证测试更多信息 引言 在现代的开发环境中,容器技术发挥着至关重要的作用。Docker 提供了快速、可靠和易于使用的容器化解决方案,使开发人员和 DevOps 专业人士能够以轻松的方式将应用程序从一个环境部署到另一个环…...

策略者模式(代码实践C++/Java/Python)————设计模式学习笔记
文章目录 1 设计目标2 Java2.1 涉及知识点2.2 实现2.2.1 实现两个接口飞行为和叫行为2.2.2 实现Duck抽象基类(把行为接口作为类成员)2.2.3 实现接口飞行为和叫行为的具体行为2.2.4 具体实现鸭子2.2.5 模型调用 3 C(用到了大量C2.0的知识&…...

vue2/Vue3项目中,通过请求接口来刷新列表中的某个字段(如:Axios)
vue2/Vue3项目中,通过请求接口来刷新列表中的某个字段。可以使用 Vue 的异步请求库(如 Axios)来发送请求,并在请求成功后更新相应的字段。 示例如下(Vue2): 简单的示例如下,假设列…...

Java多线程锁定
前言 利用多线程编程虽然能极大地提升运行效率,但是多线程本身的不稳定也会带来一系列的问题,其中最经典莫过于售票问题;这时就需要人为地加以限制和干涉已解决问题,譬如今日之主题——锁定。 锁定是我们在多线程中用来解决售票…...

【C 数据结构】单链表
文章目录 【 1. 基本原理 】1.1 链表的节点1.2 头指针、头节点、首元节点 【 2. 链表的创建 】2.0 创建1个空链表(仅有头节点)2.1 创建单链表(头插入法)*2.2 创建单链表(尾插入法) 【 3. 链表插入元素 】【…...

[MAUI]集成富文本编辑器Editor.js至.NET MAUI Blazor项目
文章目录 获取资源从源码构建从CDN获取获取扩展插件 创建项目创建控件创建Blazor组件初始化保存销毁编写渲染逻辑 实现只读/编辑功能切换模式获取只读模式状态响应切换事件 实现明/暗主题切换项目地址 Editor.js 是一个基于 Web 的所见即所得富文本编辑器,它由CodeX…...

Spring Boot | Spring Boot 整合 “Servlet三大组件“ ( Servlet / Filter / Listene )
目录: Spring Boot 整合 "Servlet三大组件" :1. 使用 "组件注册" 的方式 "整合Servlet三大组件" ( 实际操作为 : 创建自定义的"三大组件"对象 结合刚创建"的自定义组件对象"来 将 XxxRegistrationBean对象 通过…...

错误分析 (Machine Learning研习十九)
错误分析 您将探索数据准备选项,尝试多个模型,筛选出最佳模型,使用 Grid SearchCV微调其超参数,并尽可能实现自动化。在此,我们假设您已经找到了一个有前途的模型,并希望找到改进它的方法。其中一种方法就…...

SQL系统函数知识点梳理(Oracle)
这里写目录标题 函数系统函数转换函数to_date()to_char()将数值转换成字符格式 添加货币符号将日期转换成字符 其他不常用的转换函数 字符型函数连接函数大小写转换函数大写转换小写转换首字母大写,其余的小写 替换函数去除空格函数截取函数填充函数获取字符长度函数…...

面试突击---MySQL索引
面试突击---MYSQL索引 面试表达技巧:1、谈一下你对于mysql索引的理解?(为什么mysql要选择B树来存储索引)2、索引有哪些分类?3、聚簇索引与非聚簇索引4、回表、索引覆盖、最左匹配原则、索引下推(1ÿ…...

关注 | 我国已对百种产品实施强制性产品认证
市场监管总局在7日举行的新闻发布会上介绍,该局日前发布《市场监管总局关于对商用燃气燃烧器具等产品实施强制性产品认证管理的公告》,对具有较高安全风险的商用燃气燃烧器具、阻燃电线电缆、电子坐便器、电动自行车乘员头盔、可燃气体探测报警产品、水性…...

虚幻引擎架构自动化及蓝图编辑器高级开发进修班
课程名称:虚幻引擎架构自动化及蓝图编辑器高级开发进修班 课程介绍 大家好 我们即将推出一套课程 自动化系统开发。 自动化技术在项目开发的前中后期都大量运用。如何您是一家游戏公司,做的是网络游戏,是不是经常会遇到程序员打包加部署需…...

Weakly Supervised Audio-Visual Violence Detection 论文阅读
Weakly Supervised Audio-Visual Violence Detection 论文阅读 摘要III. METHODOLOGYA. Multimodal FusionB. Relation Modeling ModuleC. Training and Inference IV. EXPERIMENTSV. CONCLUSION阅读总结 文章信息: 发表于:IEEE TRANSACTIONS ON MULTIME…...

华为海思数字芯片设计笔试第六套
声明 下面的题目作答都是自己认为正确的答案,并非官方答案,如果有不同的意见,可以评论区交流。 这些题目也是笔者从各个地方收集的,感觉有些题目答案并不正确,所以在个别题目会给出自己的见解,欢迎大家讨论…...

重绘和重排:概念、区别和应用示例
还是大剑师兰特:曾是美国某知名大学计算机专业研究生,现为航空航海领域高级前端工程师;CSDN知名博主,GIS领域优质创作者,深耕openlayers、leaflet、mapbox、cesium,canvas,webgl,ech…...

创建k8s deploy yaml文件的imagePullSecrets语句
镜像仓库是harbor kubectl create secret docker-registry key --docker-server192.168.0.190 --docker-usernameadmin --docker-passwordHarbor12345...

大模型预测结果导入到Doccano,人工修正预测不准的数据
背景 使用大语言模型做实体识别的实验时,发现大模型关于实体的边界预测一直不准。 主要原因在于当时找了很多同学标注数据,由于不同组同学关于实体的边界没有统一,故导致数据集中实体边界也没统一。 (找太多人标,会有…...

python三方库_ciscoconfparse学习笔记
文章目录 介绍使用基本原理父子关系 属性ioscfg 获取配置信息,返回列表is_config_line 判断是否是配置行is_intf 判断IOSCfgLine是不是interfaceis_subintf 判断IOSCfgLine是不是子接口lineage 不知道用法is_ethernet_intf 判断IOSCfgLine是否是以太网接口is_loopback_intf 判断…...

HDFS详解(Hadoop)
Hadoop 分布式文件系统(Hadoop Distributed File System,HDFS)是 Apache Hadoop 生态系统的核心组件之一,它是设计用于存储大规模数据集并运行在廉价硬件上的分布式文件系统。 1. 分布式存储: HDFS 将文件分割成若干块…...

python创建word文档并向word中写数据
一、docx库的安装方法 python创建word文档需要用到docx库,安装命令如下: pip install python-docx 注意,安装的是python-docx。 二、使用方法 使用方法有很多,这里只介绍创建文档并向文档中写入数据。 import docxmydocdocx.Do…...

MongoDB的安装配置及使用
文章目录 前言一、MongoDB的下载、安装、配置二、检验MongoDB是否安装成功三、Navicat 操作MongoDB四、创建一个集合,存放三个文档总结 前言 本文内容: 💫 MongoDB的下载、安装、配置 💫 检验MongoDB是否安装成功 ❤️ Navicat 操…...