代理模式 (Proxy Pattern)

定义:

代理模式(Proxy Pattern)是一种结构型设计模式,它通过提供一个代理(或称代表)对象来控制对另一个对象的访问。这种模式创建了一个代理对象,用来代表实际对象的功能,从而可以在不直接访问实际对象的情况下进行操作。

代理模式的核心思想是将客户端与实际执行功能的对象分离,引入一个中介层(代理层),由代理对象来决定是否和如何访问实际对象。这种间接层可以用于多种目的,比如控制访问、延迟初始化、实现安全检查、添加额外的功能(例如日志记录、事务处理等)。

代理模式主要包含以下三个角色:

  1. 主题(Subject)接口:定义了代理和真实对象共同实现的接口,这样在任何使用真实对象的地方都可以使用代理对象。
  2. 真实主题(Real Subject)类:实际执行逻辑的类,代理模式的目的是控制对这个类的访问。
  3. 代理(Proxy)类:包含对真实主题的引用,它实现了与真实主题相同的接口,并可能在调用真实主题对象之前或之后执行额外的处理。

代理模式的常见变体包括:

  • 虚拟代理:在需要时才创建或初始化昂贵的对象。
  • 远程代理:为远程对象(如网络服务)提供本地代表。
  • 保护代理:控制对原始对象的访问,常用于权限管理。
  • 智能引用代理:在访问对象时执行额外的动作,例如计数引用次数、检查是否锁定等。

总的来说,代理模式增强了对原始对象的控制,同时保持了代码的灵活性和可扩展性。

解决的问题:
  • 控制对对象的访问:有时直接访问对象可能会带来各种问题,如安全性问题、对象正在执行耗时操作、对象需要远程访问等。代理模式允许在对象和访问者之间放置一个中介(代理),从而控制对这个对象的访问。
  • 延迟初始化(懒加载):如果对象的创建和初始化是资源密集型的,那么代理模式可以推迟这些开销大的操作直到真正需要对象时才进行。代理对象会在客户端首次请求时才实例化实际对象。
  • 实现安全控制:代理可以控制对实际对象的访问权限,进行身份验证或其他安全措施,确保只有具有适当权限的客户端才能访问对象。
  • 增加或修改原有对象的功能:代理可以在调用实际对象的方法前后添加额外的功能,比如添加日志记录、事务处理等,而不改变原有对象的代码。
  • 远程对象访问:在远程通信中,代理可以作为本地的代表来代理远程对象,隐藏实际对象位于远程位置的事实。
  • 引用计数和资源管理:代理可以跟踪对实际对象的引用次数,以便在没有引用时进行资源释放,从而实现有效的资源管理。

通过这些功能,代理模式提供了一种强大的方式来增强、控制或者以其他方式改变对对象的访问,同时保持对客户端的透明性。

使用场景:
  • 远程代理
    • 当对象位于不同的地址空间(如在远程服务器上)时,可以使用远程代理来在本地代表这个远程对象。这在分布式系统或网络服务(如远程方法调用RMI)中非常常见。
  • 虚拟代理
    • 当对象的创建开销很大时,可以使用虚拟代理进行懒加载,即在真正需要对象时才创建它。这适用于大型对象的加载和初始化,如图形重的图像或文件。
  • 保护代理
    • 用于控制对原始对象的访问权限。保护代理可以在执行操作之前进行权限检查,确保满足特定条件的客户端才能访问对象。这适用于安全敏感的操作。
  • 智能引用代理
    • 在对象被访问时执行额外的动作,例如引用计数、线程安全检查、锁定资源或者记录日志等。
  • 缓存代理
    • 用于为昂贵的操作结果提供临时存储(缓存),以便在后续请求中快速访问。这在数据密集型操作或网络请求中特别有用。
  • 界面代理
    • 当需要对用户界面元素进行控制或管理时,可以使用界面代理。比如,在UI组件完全加载之前提供占位符。
  • 日志记录代理
    • 用于记录对某个对象的操作或方法调用,这对于调试、监控或审计非常有用。
  • 事务代理
    • 在执行操作时自动开始事务,并在操作完成后提交或回滚事务,常见于数据库操作。

这些场景展示了代理模式如何在不同的环境和需求中提供灵活的解决方案,以增强或控制对对象的访问。

示例代码 1 - 简单代理:
// 步骤1:定义接口
public interface Subject {void performAction();
}// 步骤2:创建实现接口的真实类
public class RealSubject implements Subject {@Overridepublic void performAction() {System.out.println("RealSubject performAction() executed.");}
}// 步骤3:创建代理类,也实现同一个接口
public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic void performAction() {if (realSubject == null) {realSubject = new RealSubject();}preAction();realSubject.performAction();postAction();}private void preAction() {System.out.println("ProxySubject preAction() executed.");}private void postAction() {System.out.println("ProxySubject postAction() executed.");}
}// 步骤4:客户端使用代理
public class Client {public static void main(String[] args) {Subject subject = new ProxySubject();subject.performAction(); // 客户端通过代理间接地访问真实对象的方法}
}

在这个示例中:

  • Subject 接口定义了一个操作,这是客户端调用的目标。
  • RealSubject 类是实现了 Subject 接口的真实对象类,执行实际的业务逻辑。
  • ProxySubject 类同样实现了 Subject 接口,但它持有一个 RealSubject 对象的引用。它控制对 RealSubject 的访问,并可以在调用真实对象之前或之后执行一些操作(如权限检查、日志记录等)。
  • 在客户端 Client 中,客户端通过 ProxySubject 访问 RealSubject,而不是直接访问,这样就可以在不更改 RealSubject 代码的情况下,添加额外的操作。
示例代码 2 - 动态代理:

动态代理是一种在运行时创建代理对象的方式,Java中的动态代理通过实现InvocationHandler接口来实现。以下是一个简单的动态代理的示例实现:

// 首先,定义一个接口,代理对象和真实对象都将实现这个接口
public interface Subject {void performAction();
}// 接下来,创建一个真实对象类,它实现了上述接口
public class RealSubject implements Subject {@Overridepublic void performAction() {System.out.println("Performing action in RealSubject");}
}// 然后,定义一个`InvocationHandler`,这个处理器负责定义代理对象的行为:、
public class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoking " + method.getName());Object result = method.invoke(target, args);System.out.println("After invoking " + method.getName());return result;}
}// 最后,创建代理实例并使用它
public class Client {public static void main(String[] args) {RealSubject realSubject = new RealSubject();Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),new Class<?>[]{Subject.class},new DynamicProxyHandler(realSubject));proxySubject.performAction(); // 调用动态代理对象的方法}
}

在这个示例中,DynamicProxyHandler是一个InvocationHandler,它定义了如何响应代理对象上的方法调用。在调用任何方法之前和之后,都会执行DynamicProxyHandler中的代码。Proxy.newProxyInstance方法用于在运行时动态创建代理对象,它需要类加载器、一组接口以及一个InvocationHandler实例。

主要符合的设计原则:
  1. 开闭原则(Open-Closed Principle):
    • 代理模式允许在不修改原始对象代码的情况下扩展其功能。通过引入代理类,可以在不改变客户端代码的前提下增加额外的行为(如访问控制、日志记录等),从而保持原始类的开闭原则。
  2. 单一职责原则(Single Responsibility Principle):
    • 代理类专注于一个任务:控制对原始对象的访问。这样的设计意味着原始对象可以专注于其核心功能,而将控制逻辑委托给代理对象,从而遵循单一职责原则。
  3. 接口隔离原则(Interface Segregation Principle):
    • 代理类和实际对象通常实现相同的接口。这样的设计确保了代理对象可以在不影响客户端代码的情况下代替实际对象。客户端依赖于抽象接口,而不是具体实现,符合接口隔离原则。
在JDK中的应用:
  • Java反射中的动态代理(java.lang.reflect.Proxy:
    • Proxy 类和 InvocationHandler 接口支持动态创建代理实例。这种机制允许在运行时创建代理对象,用于拦截对目标对象方法的调用。动态代理广泛应用于拦截、日志记录、事务处理等场景。
  • 远程方法调用(RMI):
    • Java RMI(Remote Method Invocation)技术使用代理模式来使客户端能够透明地调用远程对象上的方法。RMI框架在客户端创建远程对象的本地代理(Stub),所有对远程对象的调用都通过这个本地代理进行。
  • java.util.Collections 中的不可修改集合:
    • Collections 类提供了如 unmodifiableListunmodifiableSetunmodifiableMap 等方法,这些方法返回给定集合的不可修改视图。这些不可修改的集合实际上是使用代理模式实现的,代理对象会拦截所有修改集合的操作并抛出异常。
  • java.lang.reflect.Proxy中的 Proxy:
    • Java的反射包中的 Proxy 类提供了创建动态代理的能力,允许开发者在运行时创建接口的代理实例,这对于实现如AOP(面向切面编程)等功能非常有用。
在Spring中的应用:
  • 面向切面编程(AOP):
    • Spring AOP 使用代理模式来实现切面的功能。它创建目标对象的代理,并在代理中织入横切关注点(例如日志、事务管理、安全等)。这样做可以将这些关注点模块化,而不需要修改实际业务逻辑代码。
    • 根据目标对象是接口还是类,Spring AOP 可以使用 JDK 动态代理或 CGLIB 代理。JDK 动态代理用于接口,而 CGLIB 代理用于没有接口的类。
  • 事务管理:
    • 在声明式事务管理中,Spring通过使用代理包装方法调用,自动地开始和结束事务。这使得开发者可以通过声明性方式管理事务,而不必在每个方法中显式地处理事务逻辑。
  • Spring Security:
    • 在Spring Security中,代理模式用于在方法调用前后进行安全检查。这允许开发者在不修改业务逻辑代码的情况下,添加或删除安全性检查。
  • 远程调用(Remote Invocation):
    • Spring还提供了对RMI(远程方法调用)的支持,允许通过代理访问远程服务。这种代理透明地处理网络通信和服务定位。

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

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

相关文章

spring boot 热部署

相信小伙伴们在日常的开发中&#xff0c;调试代码时&#xff0c;免不了经常修改代码&#xff0c;这个时候&#xff0c;为了验证效果&#xff0c;必须要重启 Spring Boot 应用。 频繁地重启应用&#xff0c;导致开发效率降低&#xff0c;加班随之而来。有没有什么办法&#xff0…

宏电股份受邀参加中国联通战新共创启航大会,共筑产业生态,链通数智未来

11月21日&#xff0c;由中国联通举办的主题为“共筑产业生态&#xff0c;链通数智未来”的网络安全现代产业链共链行动计划暨战新共创启航大会“5G工业互联网”专题供需对接会在北京顺利召开&#xff0c;宏电股份董事长左绍舟应邀出席活动。 会议现场&#xff0c;中国联通雁飞…

Rust开发——数据对象的内存布局

枚举与Sized 数据 一般数据类型的布局是其大小&#xff08;size&#xff09;、对齐方式&#xff08;align&#xff09;及其字段的相对偏移量。 1. 枚举&#xff08;Enum&#xff09;的布局&#xff1a; 枚举类型在内存中的布局通常是由编译器来确定的。不同的编译器可能有不…

centos7 系统keepalived 定时执行脚本

安装keepalived yum install -y keepalived 修改配置文件 配置文件路径 /etc/keepalived 配置文件内容 global_defs {router_id localhost.localdomain # 访问到主机&#xff0c;本机的hostname&#xff0c;需要修改 }vrrp_script chk_http_port {script "/etc/kee…

INFLOW:用于检测隐藏服务器的反向网络流水印

文章信息 论文题目&#xff1a;INFLOW: Inverse Network Flow Watermarking for Detecting Hidden Servers 期刊&#xff08;会议&#xff09;&#xff1a;IEEE INFOCOM 2018 - IEEE Conference on Computer Communications 级别&#xff1a;CCF A 文章链接&#xff1a;https:…

Docker 安装 Apache

目录 拉取官方 Apache 镜像 查看本地镜像 列出正在运行的容器 运行 Apache 容器 创建一个 HTML 文件&#xff1a;index.html 访问 Apache 拉取官方 Apache 镜像 查找 Docker Hub 上的 httpd 镜像。 可以通过 Tags 查看其他版本的 httpd&#xff0c;默认是最新版本 httpd…

人工智能学习阶段有哪些?

人工智能学习阶段有哪些? 人工智能是一个跨学科、跨领域的杂交学科&#xff0c;未来的趋势来看,人工智能的出现使人们的生活变得更美好、更便捷&#xff0c;许多小伙伴想学习人工智能&#xff0c;其实看似人工智能比较杂多&#xff0c;无从下手&#xff0c;我们只要从以下7个阶…

go build自建包报错package XXX is not in std

在构建包名的时候遇到了如下错误 C:\Users\xxx\Desktop\code\golx\src>go build example package example is not in std (D:\Go\src\example) 解决方法 修改 GO111MODULE go env -w GO111MODULEoff

SpringMVC 基础知识

学习目标 掌握基于 SpringMVC 获取请求参数与响应 json 数据操作熟练应用基于 REST 风格的请求路径设置与参数传递能够根据实际业务建立前后端开发通信协议并进行实现基于 SSM 整合技术开发任意业务模块功能 1 SpringMVC 简介 1.1 概述 1.1.1 web程序开发流程 【执行过程】…

【搭环境】装Python3.8 open3d

先装Python3.8 方法一试了找不到Python3.8的库&#xff0c;所以用方法二装上了。 Python3加入环境变量 更改Python默认指向 open3d需要Python3.6以上&#xff0c;最好用Ubuntu18版本&#xff0c;我用的16版本。。

从入门到精通!Python数据分析畅销书《利用Python进行数据分析》第三版中文版助你成为数据分析师!

Python数据分析畅销书《利用Python进行数据分析》第三版中文版助你成为数据分析师&#xff01; 个人简介什么是数据分析如何自学数据分析书籍推荐作译者简介作者简介译者简介 主要变动导读视频&#xff1a;购书链接&#xff1a;参与方式往期赠书回顾 个人简介 &#x1f3d8;️&…

【好玩的开源项目】Linux系统之部署proxx扫清黑洞小游戏

【好玩的开源项目】Linux系统之部署proxx扫清黑洞小游戏 一、proxx小游戏介绍1.1 proxx小游戏简介1.2 开源地址 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、部署Node.js环境4.1 下载Node.js安装包4.…

动态规划求二维网格中从左上角到右下角的最短路径( 每次只能向下、向右、向右下走 ) java 实现

dp[i][j] 表示在以点(0,0)作为左上角&#xff0c;点(i,i) 作为右下角的二维网格中 左上角到右下角的最短路径&#xff0c; 动态转移方程为&#xff1a;dp[i][j] min{ dp[i][j-1],dp[i-1][j],dp[i-1][j-1] }.distance weight[i][j] ImageUtils.java&#xff1a; import java.a…

vue3的 nextTick()的使用

引言&#xff1a; 当你修改了响应式状态时&#xff0c;DOM 会被自动更新。但是需要注意的是&#xff0c;DOM 更新不是同步的。Vue 会在“next tick”更新周期中缓冲所有状态的修改&#xff0c;以确保不管你进行了多少次状态修改&#xff0c;每个组件都只会被更新一次。 要等待…

三十分钟学会Shell(上)

Shell ​ Shell 本身并不是内核的一部分&#xff0c;它只是站在内核的基础上编写的一个应用程序&#xff0c;是用户和Linux文件系统之间的桥梁。Shell 有自己的特殊性&#xff0c;就是开机立马启动&#xff0c;并呈现在用户面前&#xff1b;用户通过 Shell 来使用 Linux&#x…

Windows日常故障自我排查:用工具eventvwr.msc(事件查看器)分析问题故障

windows故障排查方法一&#xff1a; 工具用法 系统故障问题时&#xff0c;找不到解决方法 首先&#xff0c; 在搜索栏输入&#xff1a; 事件查看器(eventvwr.msc) 打开程序 根据程序找到程序运行的LOG 根据程序Operational筛选出错误日志&#xff1a; 日志中找错误原因&…

假期宅家不无聊,用极米投影仪H6 Pro 4K解锁宅家多样玩法

不知道大家平时的假期是怎么度过的&#xff0c;是选择出门游玩?还是要在家好好安排自己的学习充电计划?平日工作这么累&#xff0c;不如趁假期好好休息一下&#xff0c;把以前落下的番剧补回来。不过手机的屏幕太小&#xff0c;电脑又不能躺在床上看&#xff0c;只有够大尺寸…

如何解决EMI问题 电磁干扰问题诊断四步骤

电磁干扰的观念与防制﹐在国内已逐渐受到重视。虽然目前国内并无严格管制电子产品的电磁干扰&#xff08;EMI&#xff09;﹐但由于欧美各国多已实施电磁干扰的要求﹐加上数字产品的普遍使用﹐对电磁干扰的要求已是刻不容缓的事情。笔者由于啊作的关系﹐经常遇到许多产品已完成成…

Lora学习资料汇总

目录 LoRa联盟 Semtech lora网关供应商: LoRaMAC API文档 论坛 开发板 主流技术对比分析 LoRa网络距离模拟测试方法 LoRa应用 Lora LoraWAN教程 LoRa联盟 LoRa联盟&#xff1a;LoRaWAN规范的制定组织 https://www.lora-alliance.org/ LoRa技术白皮书&#xff1a;htt…

Android扫码ZXing

1. 获取权限 请注意动态申请及重写申请结果返回方法。 <uses-permission android:name"android.permission.CAMERA"/> 2. 添加依赖 //Gradle Scripts -> build.gradle(Module:app) implementation com.google.zxing:core:3.4.1 implementation com.jour…