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

设计模式 代理模式(Proxy Pattern)

简绍

代理模式是一种结构型设计模式,它允许您提供一个替代对象(代理)来控制对一个真实对象的访问。这种模式通常用于在访问某个对象之前或之后执行一些额外的操作,比如缓存、日志记录、权限验证等

静态代理

静态代理的特点
  • 代理类和真实主题类在编译时确定:
    代理类和真实主题类都是在编写代码时就确定的,并且它们通常共享一个公共接口。
  • 代理类包含对真实主题类的引用:
    代理类包含一个对真实主题类实例的引用,并通过该引用调用真实主题的方法。
  • 代理类可以执行额外的操作:
    代理类可以在调用真实主题的方法之前或之后执行一些额外的操作,如日志记录、权限验证等。
静态代理的应用场景
  • 日志记录:
    在调用方法前后记录日志。
  • 权限验证:
    在调用方法前验证用户权限。
  • 缓存:
    缓存方法的结果,以避免重复计算。
  • 事务管理:
    在调用方法前后管理事务。
  • 性能优化:
    延迟加载或异步处理。
静态代理的缺点

静态代理的一个缺点是,对于每一个真实主题类,都需要创建一个代理类。在需要为多个类创建代理的情况下,这可能会导致大量的代理类。

创建基础调用类

public interface Mobile {void buy(String mobileName);
}

原有的实现方式

public class XiaomiProxy implements Mobile{@Overridepublic void buy(String mobileName) {System.out.println("购买到手一台: "+ mobileName);}
}

通过对原有基础类调用 ,封装之前的调用逻辑,在上面附加一层处理方式

public class PayProxy implements Mobile{private XiaomiProxy xiaomiProxy;public PayProxy(){xiaomiProxy = new XiaomiProxy();}@Overridepublic void buy(String mobileName) {System.out.println("准备刷卡购买: " + mobileName);xiaomiProxy.buy(mobileName);}
}

两种 结果, 我们可以在代理类中去进行各种数据处理

public class Main {public static void main(String[] args) {XiaomiProxy xiaomiProxy = new XiaomiProxy();xiaomiProxy.buy("小米4");System.out.println("-----------------------");PayProxy payProxy = new PayProxy();payProxy.buy("小米4");}
}

jdk动态代理

JDK 动态代理是一种在运行时生成代理类的技术,它允许您为任何实现了接口的类创建代理对象。与静态代理不同,动态代理不需要显式地编写代理类的代码,而是通过 Java 反射 API 自动生成代理类。这种方式更加灵活,因为代理类是在运行时动态生成的。

JDK 动态代理的工作原理
  • 创建 InvocationHandler 实现类:
    创建一个实现了 InvocationHandler 接口的类,该类将定义代理行为。
  • 创建代理对象:
    使用 java.lang.reflect.Proxy 类的 newProxyInstance 方法创建代理对象。
  • 调用代理对象的方法:
    通过代理对象调用方法,实际执行的是 InvocationHandler 中定义的方法。
JDK 动态代理的关键组件
  • Interface (接口):
    代理对象和真实主题对象必须实现相同的接口。
  • RealSubject (真实主题):
    这是实际执行请求的对象。
  • InvocationHandler:
    这是一个接口,它的实现类定义了代理行为。
  • Proxy (代理):
    由 java.lang.reflect.Proxy 类创建的代理对象。
invoke 方法签名

invoke 方法是 Java 反射机制中的一个核心部分,特别是在使用动态代理时。它定义在 java.lang.reflect.InvocationHandler 接口中,并且在代理对象的方法被调用时由 Java 虚拟机 (JVM) 自动触发

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  • 这是调用该 invoke 方法的代理实例对象。
    当客户端通过代理对象调用一个方法时,这个方法实际上会委托给 InvocationHandler 中的 invoke 方法。该参数可以用于获取代理对象的信息,例如使用 proxy.getClass().getName()。有时可以将代理对象返回以进行连续调用,即链式调用,因为 this 并不是代理对象本身。

  • method:
    这是在代理实例上调用的接口方法对应的 Method 实例。
    它包含了关于要调用的方法的信息,如方法名、参数类型、返回类型等。
    可以使用这个参数来决定如何处理方法调用,例如是否需要执行某些预处理或后处理逻辑。

  • args:
    这是一个 Object 数组,包含了在代理对象上调用方法时传递的参数。
    参数按照方法定义的顺序排列。
    如果方法没有参数,则此数组为空。

public class PayProxyHandle implements InvocationHandler {private final Object target;public PayProxyHandle(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return method.invoke(target, args);}
}
Proxy.newProxyInstance 方法被用来创建一个动态代理对象。以下是该方法调用的详细解释:
获取类加载器

Mobile.class.getClassLoader()
这一行获取了 Image 接口的类加载器。类加载器是负责加载类到 JVM 的对象。在大多数情况下,您可以使用类的 getClassLoader() 方法来获取类加载器,但是基本类型和 Object 类没有类加载器,它们是由特殊的 Bootstrap ClassLoader 加载的。对于普通的类和接口,通常可以使用 .class 字段来获取其类加载器。

指定接口

ew Class<?>[]{Mobile.class}
这一行创建了一个接口数组,这里只有一个接口 Image。Proxy.newProxyInstance 方法需要知道代理对象需要实现哪些接口。

提供 InvocationHandler

new PayProxyHandle(xiaomiProxy)
这一行创建了一个 PayProxyHandle 类的实例,它实现了 InvocationHandler 接口。InvocationHandler 接口定义了一个 invoke 方法,该方法会在代理对象的方法被调用时被调用。DynamicProxyHandler 类需要处理代理对象上发生的所有方法调用,并根据需要执行额外的逻辑。

public class Main {public static void main(String[] args) {Mobile o =(Mobile) Proxy.newProxyInstance(Mobile.class.getClassLoader(),new Class<?>[]{Mobile.class},new PayProxyHandle(xiaomiProxy));o.buy("小米4");}
}

JDK 动态代理是一种强大的工具,它允许您在不修改现有类的情况下为现有类添加新的行为。通过使用动态代理,您可以轻松地扩展系统的功能,同时保持代码的整洁和模块化。动态代理的一个主要优势是无需为每个真实主题类创建一个代理类,因此可以减少代码量并提高灵活性。

cglib代理。

CGLIB(Code Generation Library)是一种强大的、高性能且动态的字节码生成库。它可以在运行时创建一个指定类的新子类。这种能力使得 CGLIB 成为 AOP(面向切面编程)框架和其他需要在运行时动态创建子类的应用的理想选择。CGLIB 不依赖于接口,因此它可以用于代理没有实现接口的类。

CGLIB 代理的工作原理
  • 代理类生成:
    CGLIB 使用字节码技术在运行时动态生成代理类。
    生成的代理类是目标类的一个子类,并且覆盖了目标类的所有非最终方法。
    这意味着所有非最终方法都可以被拦截并执行额外的逻辑。
  • 方法拦截:
    CGLIB 提供了一个 MethodInterceptor 接口,该接口定义了一个 intercept 方法。
    每当代理类的方法被调用时,intercept 方法就会被触发。
    在 intercept 方法内,你可以执行预处理和后处理逻辑,以及调用原始方法。
  • 代理对象创建:
    代理对象通过 CGLIB 的 Enhancer 类创建。你提供一个 Callback 或者实现 MethodInterceptor 的对象,该对象定义了如何处理方法调用。
CGLIB 的基本使用步骤
  • 导入 CGLIB 相关依赖:
    • 如果你使用 Maven 或 Gradle,需要添加 CGLIB 的依赖到你的项目中。
  • 定义目标类:
    定义一个目标类,该类不需要实现任何接口。
  • 创建 MethodInterceptor:
    • 实现 MethodInterceptor 接口,定义 intercept 方法。
    • 在 intercept 方法中,你可以执行一些预处理操作,然后调用原始方法,最后执行后处理操作。
  • 创建代理对象:
    • 使用 CGLIB 的 Enhancer 类来创建代理对象。
    • 设置目标类、回调函数等信息。
  • 使用代理对象:
    • 使用生成的代理对象代替原始对象。

CGLIB 的优缺点

  • 优点:
    支持对所有方法的拦截,即使这些方法没有声明为 public。
    不需要目标类实现特定接口,适用于没有实现接口的类。
    高性能,尤其是在多次调用相同方法的情况下。
  • 缺点:
    对于最终类 (final) 和最终方法 (final) 无法创建代理。
    由于使用了字节码生成技术,可能会产生额外的 CPU 和内存开销。

安装 CGLIB

<dependency><groupId>net.sf.cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
intercept 方法的参数解释

intercept 方法的签名如下:

Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
  • obj: 代理对象本身。这是 CGLIB 动态生成的代理类的实例。
  • method: java.lang.reflect.Method 类型的对象,表示正在被调用的方法。
  • args: 方法调用时传递的实际参数数组。
  • proxy: MethodProxy 类型的对象,用于调用目标方法。
使用 MethodProxy 调用目标方法

MethodProxy 提供了两种主要的方式来调用目标方法:

使用 invokeSuper 方法:

Object result = proxy.invokeSuper(obj, args);
这种方式会直接调用代理对象的父类(即目标类)的方法。

使用 invoke 方法:

Object result = proxy.invoke(obj, args);
这种方式也会调用目标方法,但它会经过 CGLIB 的代理机制。
通常情况下,建议使用 invokeSuper 方法,因为它更高效并且避免了潜在的无限递归问题。

public class TargetMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object result = methodProxy.invokeSuper(o, objects);return result;}
}

在 CGLIB 中,Enhancer 类是用来创建动态代理的工具类。

// 创建 Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(TargetClass.class);
// 设置 Callback
enhancer.setCallback(new TargetMethodInterceptor());
// 创建代理对象
TargetClass proxy = (TargetClass) enhancer.create();
// 使用代理对象
proxy.test("test");

CGLIB 是一种强大的工具,可以用于创建动态代理,尤其是在不支持 JDK 动态代理的场景下(即目标类没有实现接口)。它通过字节码生成技术在运行时创建代理类,并且能够拦截所有的非最终方法。CGLIB 代理广泛应用于 AOP 框架中,例如 Spring AOP。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 一个简单的CRM客户信息管理系统,提供客户,线索,公海,联系人,跟进信息和数据统计功能(附源码)
  • Maven学习(零基础到面试)
  • 【Qt窗口】—— 浮动窗口
  • DARKTIMES集成到Sui,带来中世纪格斗大逃杀游戏体验
  • 【教程】实测np.fromiter 和 np.array 的性能
  • GCViT实战:使用GCViT实现图像分类任务(一)
  • Django+vue自动化测试平台(29)--测试平台集成playwright录制pytest文件执行
  • LeetCode 算法:杨辉三角 c++
  • Python——类和对象、继承和组合
  • 软考:软件设计师 — 17.程序设计语言与语言处理程序基础
  • IDEA: Html代码格式化
  • 【基础】Three.js中添加操作面板,GUI可视化调试(附案例代码)
  • Java-多线程IO工具类
  • MySQL入门学习-对系统数据库的常用查询
  • midwayjs 框架使用 rabbitmq 消息延迟
  • “大数据应用场景”之隔壁老王(连载四)
  • Apache的80端口被占用以及访问时报错403
  • co模块的前端实现
  • java8-模拟hadoop
  • JavaScript 奇技淫巧
  • js写一个简单的选项卡
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • mysql外键的使用
  • React Transition Group -- Transition 组件
  • React-Native - 收藏集 - 掘金
  • Wamp集成环境 添加PHP的新版本
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 创建一种深思熟虑的文化
  • 从0到1:PostCSS 插件开发最佳实践
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 大型网站性能监测、分析与优化常见问题QA
  • 给Prometheus造假数据的方法
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 检测对象或数组
  • 离散点最小(凸)包围边界查找
  • 项目实战-Api的解决方案
  • 小程序01:wepy框架整合iview webapp UI
  • 写给高年级小学生看的《Bash 指南》
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #14vue3生成表单并跳转到外部地址的方式
  • #APPINVENTOR学习记录
  • #define,static,const,三种常量的区别
  • #LLM入门|Prompt#3.3_存储_Memory
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • #使用清华镜像源 安装/更新 指定版本tensorflow
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (zhuan) 一些RL的文献(及笔记)
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)springboot人体健康检测微信小程序 毕业设计 012142
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (论文阅读32/100)Flowing convnets for human pose estimation in videos
  • (南京观海微电子)——I3C协议介绍