代理模式:控制对象访问的守门员🔐,优雅实现功能增强与访问控制!


文章目录

  • 代理模式:控制对象访问的守门员🔐,优雅实现功能增强与访问控制!
    • 前言:为什么需要代理?🤔
    • 一、代理模式:访问控制的专家 🛡️
      • 1.1 什么是代理模式?
      • 1.2 为什么需要代理模式?
    • 二、代理模式的结构:中间人的艺术 🎭
    • 三、代理模式的三种类型:静态代理、动态代理与CGLIB代理 🔄
      • 3.1 静态代理:编译时确定的代理
      • 3.2 动态代理:运行时生成的代理
      • 3.3 CGLIB代理:基于继承的代理
    • 四、代理模式实战:实际应用案例 💼
      • 4.1 图片延迟加载
      • 4.2 权限控制
      • 4.3 远程代理
    • 五、代理模式在Java标准库中的应用 📚
      • 5.1 Java动态代理
      • 5.2 Spring AOP
      • 5.3 JDBC连接池
    • 六、代理模式的优缺点与适用场景 ⚖️
      • 6.1 优点
      • 6.2 缺点
      • 6.3 适用场景
    • 七、代理模式与其他模式的对比 🔄
      • 7.1 代理模式 vs 装饰器模式
      • 7.2 代理模式 vs 适配器模式
      • 7.3 代理模式 vs 外观模式
    • 八、代理模式的最佳实践 🌟
    • 总结:代理模式,访问控制的优雅之道 🎯


前言:为什么需要代理?🤔

各位宝子们,今天我们来聊一个设计模式界的"守门员"——代理模式!😎 还在为如何控制对象访问而头疼吗?还在为如何在不修改原有代码的情况下增加功能而烦恼吗?代理模式来拯救你啦!

代理模式是设计模式家族中的"访问控制专家",它能帮我们优雅地控制对对象的访问,同时还能在不修改原有代码的情况下增加新功能。今天就带大家彻底搞懂这个"看似简单,实则强大"的设计模式!💯


一、代理模式:访问控制的专家 🛡️

1.1 什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过创建一个代理对象来控制对其他对象的访问。就像现实生活中的经纪人、律师一样,代理对象充当了客户与目标对象之间的中介,客户不直接与目标对象交互,而是通过代理对象间接交互!🤝

1.2 为什么需要代理模式?

想象一下这些场景:

  • 需要控制对敏感对象的访问权限
  • 需要在访问对象时执行额外的操作(如日志记录、性能监控)
  • 需要延迟加载大型资源对象
  • 需要在远程服务器上执行操作
  • 需要为对象添加功能,但不想修改原有代码

这些场景有什么共同点?它们都涉及到对对象访问的控制和增强。代理模式就是为这些场景量身定制的!🚀


二、代理模式的结构:中间人的艺术 🎭

代理模式包含以下几个角色:

  • 抽象主题(Subject):定义了代理对象和真实对象的共同接口,这样就可以在任何使用真实对象的地方使用代理对象
  • 真实主题(Real Subject):定义了代理对象所代表的真实对象,是最终要引用的对象
  • 代理(Proxy):保存一个引用使得代理可以访问实体,并提供一个与Subject接口相同的接口,这样代理就可以用来替代实体
// 抽象主题
public interface Subject {void request();
}// 真实主题
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("真实主题处理请求");}
}// 代理
public class Proxy implements Subject {private RealSubject realSubject;public Proxy() {this.realSubject = new RealSubject();}@Overridepublic void request() {// 前置处理preRequest();// 调用真实主题的方法realSubject.request();// 后置处理postRequest();}private void preRequest() {System.out.println("代理前置处理");}private void postRequest() {System.out.println("代理后置处理");}
}// 客户端代码
Subject proxy = new Proxy();
proxy.request();
// 输出:
// 代理前置处理
// 真实主题处理请求
// 代理后置处理

看到了吗?通过代理对象,我们可以在调用真实对象的方法前后添加自己的处理逻辑,而客户端对此一无所知!这就是代理模式的魅力所在!🎩✨


三、代理模式的三种类型:静态代理、动态代理与CGLIB代理 🔄

3.1 静态代理:编译时确定的代理

静态代理是最基础的代理模式,代理类在编译时就已经确定。上面的例子就是一个典型的静态代理。

优点:

  • 实现简单,容易理解
  • 可以在不修改目标对象的前提下扩展目标对象的功能

缺点:

  • 代理类和真实主题类都实现了相同的接口,会产生很多代理类
  • 接口增加方法时,代理类和真实主题类都要维护

3.2 动态代理:运行时生成的代理

Java的动态代理是通过反射机制在运行时动态生成代理类的代理方式。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 抽象主题
public interface Subject {void request();
}// 真实主题
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("真实主题处理请求");}
}// 动态代理处理器
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("动态代理前置处理: " + method.getName());// 调用真实主题的方法Object result = method.invoke(target, args);// 后置处理System.out.println("动态代理后置处理: " + method.getName());return result;}
}// 客户端代码
Subject realSubject = new RealSubject();
InvocationHandler handler = new DynamicProxyHandler(realSubject);// 创建动态代理
Subject proxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler
);proxy.request();
// 输出:
// 动态代理前置处理: request
// 真实主题处理请求
// 动态代理后置处理: request

优点:

  • 可以代理多个类,只需要一个代理处理器
  • 可以在运行时动态地创建代理,无需手动编写代理类

缺点:

  • 只能代理实现了接口的类
  • 反射调用方法比直接调用方法性能差

3.3 CGLIB代理:基于继承的代理

CGLIB(Code Generation Library)是一个强大的高性能代码生成库,可以在运行时扩展Java类并实现接口。CGLIB通过生成目标类的子类来实现代理。

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 RealSubject {public void request() {System.out.println("真实主题处理请求");}
}// CGLIB代理拦截器
public class CglibProxyInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 前置处理System.out.println("CGLIB代理前置处理: " + method.getName());// 调用真实主题的方法Object result = proxy.invokeSuper(obj, args);// 后置处理System.out.println("CGLIB代理后置处理: " + method.getName());return result;}
}// 客户端代码
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new CglibProxyInterceptor());// 创建CGLIB代理
RealSubject proxy = (RealSubject) enhancer.create();proxy.request();
// 输出:
// CGLIB代理前置处理: request
// 真实主题处理请求
// CGLIB代理后置处理: request

优点:

  • 可以代理没有实现接口的类
  • 性能比JDK动态代理更好

缺点:

  • 不能代理final类和final方法
  • 需要引入第三方库

四、代理模式实战:实际应用案例 💼

4.1 图片延迟加载

想象一个图片查看器应用,加载高清图片可能需要很长时间。我们可以使用代理模式来实现图片的延迟加载,只有当真正需要显示图片时才加载图片数据。

// 图片接口
public interface Image {void display();
}// 真实图片
public class RealImage implements Image {private String filename;public RealImage(String filename) {this.filename = filename;loadFromDisk();}private void loadFromDisk() {System.out.println("加载图片: " + filename);}@Overridepublic void display() {System.out.println("显示图片: " + filename);}
}// 代理图片
public class ProxyImage implements Image {private RealImage realImage;private String filename;public ProxyImage(String filename) {this.filename = filename;}@Overridepublic void display() {if (realImage == null) {realImage = new RealImage(filename);}realImage.display();}
}// 客户端代码
Image image = new ProxyImage("高清图片.jpg");// 图片未加载
System.out.println("图片未加载");// 图片加载并显示
image.display();// 再次显示图片(不会重新加载)
image.display();

这个例子展示了代理模式如何实现延迟加载,只有在真正需要时才创建昂贵的对象,提高了系统性能!🚀

4.2 权限控制

代理模式可以用来实现权限控制,只有具有特定权限的用户才能访问某些资源。

// 文档接口
public interface Document {void read();void write();
}// 真实文档
public class RealDocument implements Document {private String name;public RealDocument(String name) {this.name = name;}@Overridepublic void read() {System.out.println("读取文档: " + name);}@Overridepublic void write() {System.out.println("写入文档: " + name);}
}// 权限控制代理
public class ProtectionProxy implements Document {private RealDocument realDocument;private String userRole;public ProtectionProxy(String documentName, String userRole) {this.realDocument = new RealDocument(documentName);this.userRole = userRole;}@Overridepublic void read() {// 所有用户都可以读取文档realDocument.read();}@Overridepublic void write() {// 只有管理员可以写入文档if ("admin".equals(userRole)) {realDocument.write();} else {System.out.println("权限不足,无法写入文档");}}
}// 客户端代码
Document adminDocument = new ProtectionProxy("敏感文件.txt", "admin");
adminDocument.read();  // 可以读取
adminDocument.write(); // 可以写入Document userDocument = new ProtectionProxy("敏感文件.txt", "user");
userDocument.read();  // 可以读取
userDocument.write(); // 权限不足,无法写入

这个例子展示了代理模式如何实现权限控制,保护敏感资源不被未授权的用户访问!🔒

4.3 远程代理

远程代理可以隐藏远程对象的复杂性,使客户端感觉像是在调用本地对象。

// 服务接口
public interface Service {String performAction(String data);
}// 远程服务实现
public class RemoteService implements Service {@Overridepublic String performAction(String data) {return "处理数据: " + data;}
}// 远程代理
public class RemoteProxy implements Service {private Service remoteService;public RemoteProxy() {// 在实际应用中,这里会通过网络连接到远程服务// 这里简化为直接创建远程服务对象this.remoteService = new RemoteService();}@Overridepublic String performAction(String data) {System.out.println("远程代理: 准备发送数据到远程服务");// 在实际应用中,这里会通过网络调用远程服务String result = remoteService.performAction(data);System.out.println("远程代理: 接收到远程服务的响应");return result;}
}// 客户端代码
Service service = new RemoteProxy();
String result = service.performAction("测试数据");
System.out.println("结果: " + result);

这个例子展示了远程代理如何隐藏远程调用的复杂性,使客户端感觉像是在调用本地对象!🌐


五、代理模式在Java标准库中的应用 📚

5.1 Java动态代理

Java的java.lang.reflect.Proxy类提供了创建动态代理的功能,这是Java标准库中代理模式的直接应用。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 创建动态代理的工厂方法
public static <T> T createProxy(T target, Class<?>... interfaces) {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),interfaces,new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("调用方法: " + method.getName());return method.invoke(target, args);}});
}

5.2 Spring AOP

Spring的面向切面编程(AOP)就是基于代理模式实现的。Spring AOP使用JDK动态代理或CGLIB来创建目标对象的代理,从而实现方法拦截和横切关注点的模块化。

// Spring AOP配置示例
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {@Beanpublic MyAspect myAspect() {return new MyAspect();}
}// 切面定义
@Aspect
public class MyAspect {@Before("execution(* com.example.service.*.*(..))") // 切点表达式public void before(JoinPoint joinPoint) {System.out.println("前置通知: " + joinPoint.getSignature().getName());}
}

5.3 JDBC连接池

JDBC连接池(如Apache DBCP、HikariCP等)使用代理模式来包装数据库连接,以便在连接关闭时将其返回到池中,而不是真正关闭。

// 简化的JDBC连接池代理示例
public class ConnectionProxy implements Connection {private Connection realConnection;private ConnectionPool pool;public ConnectionProxy(Connection realConnection, ConnectionPool pool) {this.realConnection = realConnection;this.pool = pool;}@Overridepublic void close() throws SQLException {// 不真正关闭连接,而是将其返回到连接池pool.releaseConnection(this);}// 其他Connection方法的代理实现// ...
}

六、代理模式的优缺点与适用场景 ⚖️

6.1 优点

  • 职责清晰:真实主题就是实现实际的业务逻辑,不用关心其他非本职责的事务
  • 高扩展性:在不修改目标对象的前提下,可以通过代理对象扩展目标对象的功能
  • 智能化:代理类可以在调用目标方法前后做一些额外工作,如权限控制、日志记录等

6.2 缺点

  • 增加复杂度:引入代理模式会增加系统的复杂度
  • 请求处理速度可能变慢:因为代理对象会对请求进行一些处理,可能会导致请求处理速度变慢
  • 实现复杂:某些代理模式的实现(如动态代理)可能比较复杂

6.3 适用场景

  • 远程代理:为远程对象提供本地代表
  • 虚拟代理:延迟加载大型资源对象
  • 保护代理:控制对敏感对象的访问
  • 智能引用:在访问对象时执行额外操作
  • 缓存代理:为开销大的运算结果提供临时存储

七、代理模式与其他模式的对比 🔄

7.1 代理模式 vs 装饰器模式

  • 代理模式:关注的是控制对对象的访问,可能不会添加新功能
  • 装饰器模式:关注的是动态地给对象添加新功能,不改变其接口

7.2 代理模式 vs 适配器模式

  • 代理模式:提供相同的接口,控制对对象的访问
  • 适配器模式:提供不同的接口,使不兼容的接口可以一起工作

7.3 代理模式 vs 外观模式

  • 代理模式:代理与真实对象实现相同的接口,一般只代理一个对象
  • 外观模式:为子系统提供一个简化的接口,通常涉及多个对象

八、代理模式的最佳实践 🌟

  1. 选择合适的代理类型:根据需求选择静态代理、动态代理或CGLIB代理
  2. 保持接口的一致性:代理对象应该与真实对象实现相同的接口
  3. 注意性能影响:代理可能会影响性能,特别是在频繁调用的场景下
  4. 避免过度代理:不要创建代理的代理的代理…
  5. 考虑线程安全:在多线程环境下,确保代理的线程安全性

总结:代理模式,访问控制的优雅之道 🎯

代理模式是一种非常实用的设计模式,它让我们可以在不修改原有代码的情况下,控制对对象的访问,同时还能增加新功能。它在Java生态系统中应用广泛,从JDK的动态代理到Spring的AOP,再到各种连接池的实现,都能看到代理模式的身影。

在实际开发中,当你需要控制对对象的访问,或者在不修改原有代码的情况下增加新功能时,代理模式是一个非常好的选择!记住,好的设计模式就像好的工具一样,用在对的地方才能发挥最大的作用!🌈

下次当你想要控制对对象的访问时,先问问自己:“我是应该直接访问对象呢,还是应该使用代理模式呢?” 如果你需要在访问对象时执行额外的操作,那么代理模式可能是更好的选择!💪


希望这篇文章对你理解代理模式有所帮助!如果有任何问题,欢迎在评论区留言讨论!👇

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

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

相关文章

《人间词话》PPT课件

《人间词话》简介 《人间词话》是王国维所著的一部文学批评著作。《人间词话》作于1908&#xff5e;1909年&#xff0c;最初发表于《国粹学报》。该作是作者接受了西洋美学思想之洗礼后&#xff0c;以崭新的眼光对中国旧文学所作的评论。 《人间词话》PPT课件下载 夸克网盘分享…

解剖智能运维三基石:Metrics/Logs/Traces

3秒知识卡 三基石关系&#xff1a; Metrics&#xff08;指标&#xff09;→ 系统脉搏&#xff08;CPU/错误率&#xff09; Logs&#xff08;日志&#xff09;→ 事件日记&#xff08;错误堆栈/用户行为&#xff09; Traces&#xff08;追踪&#xff09;→ 血缘地图&#xff08;…

从代码学习深度学习 - 情感分析:使用卷积神经网络 PyTorch版

文章目录 前言加载数据集一维卷积最大时间汇聚层textCNN模型定义模型加载预训练词向量训练和评估模型总结前言 在之前的章节中,我们探讨了如何使用循环神经网络(RNN)来处理序列数据。今天,我们将探索另一种强大的模型——卷积神经网络(CNN)——并将其应用于自然语言处理…

深入解析分布式训练基石:ps-lite源码实现原理

分布式机器学习框架是现代推荐、广告和搜索系统的核心支撑。面对海量训练数据和高维稀疏特征&#xff0c;参数服务器&#xff08;Parameter Server, PS&#xff09; 架构应运而生。作为早期经典实现的ps-lite因其简洁性和完整性&#xff0c;成为理解PS原理的绝佳切入点。本文将…

IDEA 插件开发:Internal Actions 与 UI Inspector 快速定位 PSI

在开发 IntelliJ 平台插件的过程中&#xff0c;你常常需要搞清楚 某个 IDE 弹框背后是如何操作 PSI&#xff08;Program Structure Interface&#xff09; 的。下面这篇笔记将介绍如何通过 Internal Actions、UI Inspector 以及调试技巧快速定位 PSI 调用链。 1. 启用 Internal…

26考研|数学分析:多元函数微分学

前言 本章我们将进行多元函数微分学的学习&#xff0c;多元函数微分学与一元函数微分学相对应&#xff0c;涉及到可微性、中值定理、泰勒公式等诸多问题的探讨与研究&#xff0c;本章难度较大&#xff0c;在学习过程中需要进行深度思考与分析&#xff0c;才能真正掌握这一章的…

数星星--二分

https://www.matiji.net/exam/brushquestion/17/4498/F16DA07A4D99E21DFFEF46BD18FF68AD 二分思路不难&#xff0c;关键的区间内个数的确定 #include<bits/stdc.h> using namespace std; #define N 100011 #define inf 0x3f3f3f3f typedef long long ll; typedef pair&…

Oracle/PostgreSQL/MSSQL/MySQL函数实现对照表

函数列表清单 函数作用OraclePOSTGRESQLMSSQLMYSQL求字符串长度LENGTH(str)LENGTH(str)LEN(str)LENGTH(str)字符切割SUBSTR(str,index,length)SUBSTR(str,index,length)SUBSTRING(str,index,length)SUBSTRING(str,index,length)字符串连接str1||str2||str3...strNstr1||str2||…

pycharm客户端安装教程

二、 pycharm客户端安装 打开pycharm官网&#xff1a;https://www.jetbrains.com/pycharm/download/?sectionwindows 选择其他版本 选择2018社区版本&#xff0c;点击下载 双击下载的安装程序(第一个弹框允许)&#xff0c;选择下一步 更改安装路径&#xff0c;在pycah…

博图SCL语言中用户自定义数据类型(UDT)使用详解

博图SCL语言中用户自定义数据类型&#xff08;UDT&#xff09;使用详解 一、UDT概述 用户自定义数据类型&#xff08;UDT&#xff09;是TIA Portal中强大的结构化工具&#xff0c;允许将多个相关变量组合成单一数据结构。UDT本质是可重用的数据模板&#xff0c;具有以下核心优…

Vscode自定义代码快捷方式

首选项>配置代码片段 >新建全局代码片段 (也可以选择你的语言 为了避免有的时候不生效 选择全局代码) {"console.log": { //名字"prefix": "log",//prefix 快捷键 &#xff1a; log"body": ["console.log($1);", //b…

ESP32 008 MicroPython Web框架库 Microdot 实现的网络文件服务器

以下是整合了所有功能的完整 main.py(在ESP32 007 MicroPython 适用于 Python 和 MicroPython 的小型 Web 框架库 Microdot基础上)&#xff0c;实现了&#xff1a; Wi‑Fi 自动连接&#xff08;支持静态 IP&#xff09;&#xff1b;SD 卡挂载&#xff1b;从 /sd/www/ 读取 HTML…

Mcp-git-ingest Quickstart

目录 配置例子 文档github链接&#xff1a;git_ingest.md 配置 {"mcpServers": {"mcp-git-ingest": {"command": "uvx","args": ["--from", "githttps://github.com/adhikasp/mcp-git-ingest", "…

(LeetCode 面试经典 150 题) 27.移除元素

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 核心思路&#xff1a; 思路详解&#xff1a; 样例模拟&#xff1a; 代码&#xff1a; C代码&#xff1a; Java代码&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 题目链接…

MySQL之事务原理深度解析

MySQL之事务原理深度解析 一、事务基础&#xff1a;ACID特性的本质1.1 事务的定义与核心作用1.2 ACID特性的内在联系 二、原子性与持久性的基石&#xff1a;日志系统2.1 Undo Log&#xff1a;原子性的实现核心2.2 Redo Log&#xff1a;持久性的保障2.3 双写缓冲&#xff08;Dou…

JUC:5.start()与run()

这两个方法都可以使线程进行运行&#xff0c;但是start只能用于第一次运行线程&#xff0c;后续要继续运行该线程需要使用run()方法。如果多次运行start()方法&#xff0c;会出现报错。 初次调用线程使用run()方法&#xff0c;无法使线程运行。 如果你对一个 Thread 实例直接调…

微服务中解决高并发问题的不同方法!

如果由于流量大而在短时间内几乎同时发出请求&#xff0c;或者由于服务器不稳定而需要很长时间来处理请求&#xff0c;并发问题可能会导致数据完整性问题。 示例问题情况 让我们假设有一个逻辑可以检索产品的库存并将库存减少一个&#xff0c;如上所述。此时&#xff0c;两个请…

【2025CCF中国开源大会】OpenChain标准实践:AI时代开源软件供应链安全合规分论坛重磅来袭!

点击蓝字 关注我们 CCF Opensource Development Committee 在AI时代&#xff0c;软件供应链愈发复杂&#xff0c;从操作系统到开发框架&#xff0c;从数据库到人工智能工具&#xff0c;开源无处不在。AI 与开源生态深度融合&#xff0c;在为软件行业带来前所未有的创新效率的同…

[Java实战]springboot3使用JDK21虚拟线程(四十)

[Java实战]springboot3使用JDK21虚拟线程(四十) 告别线程池爆满、内存溢出的噩梦!JDK21 虚拟线程让高并发连接变得触手可及。本文将带你深入实战,见证虚拟线程如何以极低资源消耗轻松应对高并发压测。 一、虚拟线程 传统 Java 线程(平台线程)与 OS 线程 1:1 绑定,创建和…

SpringBoot 中使用 @Async 实现异步调用​

​ ​ SpringBoot 中使用 Async 实现异步调用 一、Async 注解的使用场合​二、Async 注解的创建与调试​三、Async 注解的注意事项​四、总结​ 在高并发、高性能要求的应用场景下&#xff0c;异步处理能够显著提升系统的响应速度和吞吐量。Spring Boot 提供的 Async 注解为开…