当前位置: 首页 > news >正文

代理模式:控制对象访问的守门员[特殊字符],优雅实现功能增强与访问控制!

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


文章目录

  • 代理模式:控制对象访问的守门员🔐,优雅实现功能增强与访问控制!
    • 前言:为什么需要代理?🤔
    • 一、代理模式:访问控制的专家 🛡️
      • 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.lqws.cn/news/500851.html

相关文章:

  • 嵌入式Linux驱动开发基础-1 hello驱动
  • 【大模型问题】ms-swift微调时,显存持续增长原因分析与解决方案
  • 【CS创世SD NAND征文】基于全志V3S与CS创世SD NAND的物联网智能路灯网关数据存储方案
  • Nginx负载均衡
  • Docker 数据持久化完全指南:Volume、Bind Mount 与匿名卷
  • OpenCV CUDA模块设备层-----创建一个“常量指针访问器” 的工具函数constantPtr()
  • Docker Compose与私有仓库部署
  • Vue3+TypeScript移动端H5播放器选型指南:M3U8与主流播放器深度解析
  • 聚宽量化——股票时间序列函数
  • 传统消防演练与 VR 消防演练的区别有哪些
  • Unreal5从入门到精通之如何录制360°VR全景视频
  • Python-3-数据结构(列表)
  • Android edge-to-edge兼容适配
  • 监管报送面试回答思路和示例
  • Learning Dynamic Prompts for All-in-One Image Restoration
  • 利用 Python 脚本批量查找并删除指定 IP 的 AWS Lightsail 实例
  • 数据采集合规安全是品牌控价基石
  • 【unitrix】 4.3 左移运算(<<)的实现(shl.rs)
  • Jupyter Notebook 完全指南:从入门到生产力工具
  • 【格与代数系统】示例2
  • 在训练词编码模型使用mask还是自回归,在训练生成大模型采用mask还是自回归?
  • 【格与代数系统】示例
  • linux 下 Doris 单点部署
  • 优化 ArcPy 脚本性能
  • 华为云 Flexus+DeepSeek 征文|基于 CCE 集群部署 Dify 平台工作流:科研论文翻译与 SEO 优化工具的全流程设计实践
  • python中学物理实验模拟:平抛运动和抛物运动
  • 基于 SpringBoot+JSP 的医疗预约与诊断系统设计与实现
  • JavaWeb学习——day8(MVC模式与session、cookie)
  • Node.js特训专栏-实战进阶:7.Express模板引擎选型与使用
  • Java SE - 图书管理系统模拟实现