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

代理模式(Proxy Pattern)

文章目录

    • 1. 概述
      • 1.1 基本概念
      • 1.2 为什么需要代理模式
      • 1.3 代理模式的四个角色
    • 2. 代理模式的类型
      • 2.1 静态代理
      • 2.2 JDK动态代理
      • 2.3 CGLIB动态代理
    • 3. 代理模式的UML类图和基本实现
      • 3.1 UML类图
      • 3.2 基本实现
        • 3.2.1 静态代理基本实现
        • 3.2.2 JDK动态代理基本实现
        • 3.2.3 CGLIB动态代理基本实现
      • 3.3 三种代理方式的对比
    • 4. 代理模式的详细示例(Java实现)
      • 4.1 场景描述
      • 4.2 静态代理示例
      • 4.3 JDK动态代理示例
      • 4.4 CGLIB动态代理示例
      • 4.5 保护代理示例
    • 5. 代理模式在Spring AOP中的应用
      • 5.1 Spring AOP的代理机制
      • 5.2 Spring AOP示例
      • 5.3 Spring AOP代理原理分析
    • 6. 代理模式与其他设计模式的区别与联系
      • 6.1 代理模式 vs 装饰器模式
        • 示例对比:
      • 6.2 代理模式 vs 适配器模式
        • 示例对比:
      • 6.3 代理模式 vs 中介者模式
    • 7. 代理模式的实际应用场景
      • 7.1 远程代理(Remote Proxy)
      • 7.2 虚拟代理(Virtual Proxy)
      • 7.3 保护代理(Protection Proxy)
      • 7.4 缓存代理(Cache Proxy)
      • 7.5 智能引用(Smart Reference)
    • 8. 代理模式的优缺点总结
      • 8.1 优点
      • 8.2 缺点
    • 9. 常见问题与解答
      • 9.1 为什么要使用代理模式而不是直接修改原始类?
      • 9.2 静态代理与动态代理应该如何选择?
      • 9.3 Spring AOP使用的是什么代理模式?
      • 9.4 CGLIB代理和JDK动态代理有什么区别?
    • 10. 总结

1. 概述

代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象(称为"被代理对象"或"目标对象")的访问。代理对象充当了客户端和目标对象之间的中介,拦截对目标对象的所有调用。

1.1 基本概念

代理模式的核心思想是:通过引入一个新的代理对象来间接访问目标对象,从而可以在不修改目标对象的前提下,增强或控制对目标对象的访问

在代理模式中,代理类通常具有以下特点:

  • 与目标对象实现相同的接口
  • 持有目标对象的引用
  • 可以在调用目标对象的方法前后执行额外的逻辑

1.2 为什么需要代理模式

代理模式的主要目的是在不改变目标对象的情况下,通过代理对象控制对目标对象的访问。这种控制可以出于多种原因:

  1. 延迟初始化:当创建目标对象的代价很高,或者目标对象可能不会被使用时,可以使用代理推迟目标对象的创建
  2. 访问控制:对目标对象的访问权限进行控制,例如添加身份验证
  3. 日志记录:记录对目标对象的调用
  4. 性能优化:例如通过缓存结果来减少重复操作
  5. 远程代理:隐藏目标对象位于不同地址空间的事实
  6. 智能引用:在访问对象时执行额外的操作,如引用计数和线程安全检查

1.3 代理模式的四个角色

  1. 抽象主题(Subject):定义代理类和真实主题的共同接口,这样在任何使用真实主题的地方都可以使用代理
  2. 真实主题(Real Subject):代理所代表的真实对象,是最终要引用的对象
  3. 代理(Proxy):包含对真实主题的引用,从而可以访问它;提供与真实主题相同的接口,以便在任何时候都可以替代真实主题;控制对真实主题的访问,并可能负责创建和删除它
  4. 客户端(Client):通过代理间接地访问真实主题

2. 代理模式的类型

代理模式根据实现方式主要分为三种类型:

2.1 静态代理

静态代理是在程序运行前,代理类的源码已经创建好,并且编译生成了代理类的.class文件。代理类和被代理类都实现同一个接口,并且在代理类中持有被代理对象的引用。

特点

  • 在编译期就已经确定了代理关系
  • 为每个被代理类都需要创建一个代理类
  • 所有的代理类都在编译期生成

缺点

  • 当接口增加方法时,目标对象与代理对象都要维护
  • 当需要代理多个类时,需要创建多个代理类,代码量大,不易维护

2.2 JDK动态代理

JDK动态代理是Java语言自带的动态代理机制,它只能代理实现了接口的类。JDK动态代理通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口实现。

特点

  • 在运行时动态生成代理类
  • 代理类实现了与目标类相同的接口
  • 目标类必须实现接口
  • 利用反射机制实现

2.3 CGLIB动态代理

CGLIB(Code Generation Library)是一个开源项目,它是一个强大的高性能高质量的代码生成库,可以在运行期扩展Java类和实现Java接口。CGLIB代理是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中的方法。

特点

  • 在运行时动态生成代理类
  • 代理类是目标类的子类
  • 目标类不需要实现接口
  • 不能代理final类和final方法
  • 性能优于JDK动态代理

3. 代理模式的UML类图和基本实现

3.1 UML类图

代理模式的UML类图如下:

+----------------+       +----------------+
|    Subject     |<------|     Client     |
+----------------+       +----------------+
| +operation()   |
+-------^--------+||
+-------+--------+       +----------------+
|  RealSubject   |<------|     Proxy      |
+----------------+       +----------------+
| +operation()   |       | -realSubject   |
+----------------+       | +operation()   |+----------------+

UML类图解释:

  1. Subject(抽象主题):定义了RealSubject和Proxy的共同接口,这样就可以在任何使用RealSubject的地方使用Proxy
  2. RealSubject(真实主题):定义了Proxy所代表的真实对象
  3. Proxy(代理):持有对RealSubject的引用,实现了与RealSubject相同的接口,并控制对RealSubject的访问
  4. Client(客户端):通过Proxy间接访问RealSubject

3.2 基本实现

以下是各种代理模式的基本实现:

3.2.1 静态代理基本实现
// 抽象主题接口
interface Subject {void operation();
}// 真实主题
class RealSubject implements Subject {@Overridepublic void operation() {System.out.println("真实主题执行操作");}
}// 代理类
class Proxy implements Subject {private RealSubject realSubject;public Proxy() {this.realSubject = new RealSubject();}@Overridepublic void operation() {// 调用前的额外操作System.out.println("调用前的处理");// 调用真实主题的方法realSubject.operation();// 调用后的额外操作System.out.println("调用后的处理");}
}// 客户端
public class Client {public static void main(String[] args) {Subject subject = new Proxy();subject.operation();}
}

输出结果:

调用前的处理
真实主题执行操作
调用后的处理
3.2.2 JDK动态代理基本实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 抽象主题接口
interface Subject {void operation();
}// 真实主题
class RealSubject implements Subject {@Overridepublic void operation() {System.out.println("真实主题执行操作");}
}// 调用处理器
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("调用前的处理");// 调用真实主题的方法Object result = method.invoke(target, args);// 调用后的额外操作System.out.println("调用后的处理");return result;}
}// 客户端
public class Client {public static void main(String[] args) {// 创建真实主题RealSubject realSubject = new RealSubject();// 创建调用处理器InvocationHandler handler = new DynamicProxyHandler(realSubject);// 创建动态代理Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler);// 调用代理对象的方法proxySubject.operation();// 查看代理类的类型System.out.println("代理类的类型:" + proxySubject.getClass().getName());}
}

输出结果:

调用前的处理
真实主题执行操作
调用后的处理
代理类的类型:com.sun.proxy.$Proxy0
3.2.3 CGLIB动态代理基本实现

CGLIB动态代理需要添加CGLIB库的依赖(如果使用Maven):

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

Java代码实现:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 真实主题(不需要实现接口)
class RealSubject {public void operation() {System.out.println("真实主题执行操作");}
}// CGLIB方法拦截器
class CglibProxyInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 调用前的额外操作System.out.println("调用前的处理");// 调用真实主题的方法Object result = proxy.invokeSuper(obj, args);// 调用后的额外操作System.out.println("调用后的处理");return result;}
}// 客户端
public class Client {public static void main(String[] args) {// 创建Enhancer对象Enhancer enhancer = new Enhancer();// 设置代理类的父类enhancer.setSuperclass(RealSubject.class);// 设置回调函数enhancer.setCallback(</

相关文章:

  • vue3 excel文件导入
  • 贝叶斯分类器:原理、算法与应用详解
  • 位运算,状态压缩dp(算法竞赛进阶指南学习笔记)
  • 【Java】接口interface学习
  • 残烛与风儿的对话
  • OC底层原理【一】 alloc init new
  • java单元测试不能点击run运行测试方法
  • 【第二天】一月速通Python第二天,函数,数据容器,列表,元组,字典。
  • 论文阅读:2023 arxiv A Survey of Reinforcement Learning from Human Feedback
  • 集成运放的关键技术参数
  • 7.0/Q1,Charls最新文章解读
  • 【Oracle专栏】Oracle中的虚拟列
  • pnpm确认全局下载安装了还是显示cnpm不是内部或外部命令,也不是可运行的程序
  • 算法分析传输加密数据格式密文存储代码混淆逆向保护
  • Mac上Cursor无法安装插件解决方法
  • 【大模型】RAG(Retrieval-Augmented Generation)检索增强生成
  • 使用 NEAT 进化智能体解决 Gymnasium 强化学习环境
  • 分布类相关的可视化图像
  • 从内核到用户态:Linux信号内核结构、保存与处理全链路剖析
  • DMA映射
  • 专访|前伊核谈判顾问:伊朗不信任美国,任何核协议都会有中俄参与
  • 佩索阿稳定常销,陀翁不断加印,青少年喜欢黑塞
  • 禾赛:车载激光雷达走向标配,核心能力可在机器人领域复用
  • 普京签署法律,诋毁俄军将面临最高7年监禁
  • 中保协:当前普通型人身保险产品预定利率研究值为2.13%
  • 言短意长|把水搅浑的京东和美团