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

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

文章目录

    • 简绍
    • 静态代理
            • 静态代理的特点
            • 静态代理的应用场景
            • 静态代理的缺点
    • jdk动态代理
            • JDK 动态代理的工作原理
            • JDK 动态代理的关键组件
            • invoke 方法签名
            • Proxy.newProxyInstance 方法被用来创建一个动态代理对象。以下是该方法调用的详细解释:
            • 获取类加载器
            • 指定接口
            • 提供 InvocationHandler
    • cglib代理。
        • CGLIB 代理的工作原理
        • CGLIB 的基本使用步骤
            • intercept 方法的参数解释
        • 使用 MethodProxy 调用目标方法
            • 使用 invokeSuper 方法:
            • 使用 invoke 方法:

简绍

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

静态代理

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

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

创建基础调用类

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

原有的实现方式

public class XiaomiProxy implements Mobile{

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

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

public class PayProxy implements Mobile{

    private XiaomiProxy xiaomiProxy;

    public PayProxy(){
        xiaomiProxy = new XiaomiProxy();
    }


    @Override
    public 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;
    }

    @Override
    public 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 {
    @Override
    public 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。


http://www.kler.cn/a/291956.html

相关文章:

  • Android - Pixel 6a 手机OS 由 Android 15 降级到 Android 14 操作记录
  • 传奇996_24——变量lua
  • 使用Java绘制图片边框,解决微信小程序map组件中marker与label层级关系问题,label增加外边框后显示不能置与marker上面
  • eBPF on Go
  • 移除元素(leetcode 27)
  • outline 分析
  • fast-voice-assistant
  • C++ 在变量前面加上(void)的作用
  • Jsoncpp的安装与使用
  • 【免越狱】iOS任意版本号APP下载
  • Apache Spark简介
  • 振动分析-26-频域分析之深入理解功率谱和功率谱密度的计算过程
  • 全国大学生数学建模竞赛全国奖项评阅工作规范(2023年修订稿)
  • 干货分享|分享一款免费开源的动态壁纸软件 Lively Wallpaper
  • LeetCode491 非递减子序列
  • Docker部署项目时的服务端口设置——给容器添加新端口映射
  • Android Studio gradle下载太慢了!怎么办?(已解决)
  • Leetcode每日刷题之155.最小栈
  • FPGA——VGA协议
  • 【Redis】深入解析 Redis 事务:特性、操作及其与 MySQL 事务的区别
  • Fiddler安卓设备抓包基础
  • ffmpeg音视频开发从入门到精通——ffmpeg下载编译与安装
  • iOS App快捷指令(App Intents)在系统搜索服务中注册shortcuts
  • Monkey日志ANR、CRASH、空指针异常及其他异常数据分析
  • 文件夹图标工具类 - C#小函数类推荐
  • JS中的【Symbol】全面讲解