Java中代理的实现方式

Java中代理的实现方式

在这里插入图片描述

在Java中,有多种方式可以实现代理,包括:

  1. 静态代理

    • 创建一个代理类,实现与目标类相同的接口或继承与目标类相同的父类。
    • 在代理类中持有一个目标类的引用。
    • 在代理类的方法中,调用目标类的对应方法,并在其前后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类间接调用目标类的方法。
  2. JDK动态代理

    • 定义一个接口,作为代理类和目标类的共同接口。
    • 创建一个实现InvocationHandler接口的代理类。
    • 通过Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。
    • 在代理类的invoke()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的invoke()方法间接调用目标类的方法。
  3. CGLIB动态代理

    • 引入CGLIB库的依赖。
    • 创建一个实现MethodInterceptor接口的代理类。
    • 通过Enhancer类创建代理对象,设置目标类作为父类、代理类作为回调对象。
    • 在代理类的intercept()方法中,可以在调用目标方法之前和之后执行额外的逻辑。
    • 使用代理对象来调用方法,实际上是通过代理类的intercept()方法间接调用目标类的方法。

这些代理方式各有特点,可以根据具体需求选择适合的方式。静态代理相对简单,但需要为每个目标类编写一个代理类;JDK动态代理适用于基于接口的代理;CGLIB动态代理适用于没有实现接口的类的代理。

1. 静态代理

// 定义接口
interface Subject {
    void doSomething();
}

// 目标类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

// 代理类
class ProxySubject implements Subject {
    private RealSubject realSubject;

    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void doSomething() {
        System.out.println("ProxySubject is doing something before RealSubject.");
        realSubject.doSomething();
        System.out.println("ProxySubject is doing something after RealSubject.");
    }
}

// 使用示例
public class StaticProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.doSomething();
    }
}

在上面的示例中,Subject是一个接口,RealSubject是目标类,实现了Subject接口。ProxySubject是代理类,也实现了Subject接口。在ProxySubject中,我们持有一个RealSubject的引用,并在调用doSomething()方法之前和之后执行额外的逻辑。

在StaticProxyExample中,我们创建了一个RealSubject对象和一个ProxySubject对象,并调用proxySubject.doSomething()方法。在执行过程中,ProxySubject会在调用RealSubject的doSomething()方法之前和之后输出额外的信息,从而实现对目标类的增强。

这就是一个简单的静态代理示例,通过代理类包装目标类,实现了对目标类的功能增强。

2. JDK动态代理

首先,定义一个接口和一个实现该接口的目标类:

// 定义接口
interface Subject {
    void doSomething();
}

// 实现接口的目标类
class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

然后,创建一个实现InvocationHandler接口的代理类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 代理类
class ProxyHandler implements InvocationHandler {
    private Object target;

    public ProxyHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Proxy is doing something before RealSubject.");
        Object result = method.invoke(target, args);
        System.out.println("Proxy is doing something after RealSubject.");
        return result;
    }
}

在代理类的invoke()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。
最后,创建代理对象并调用方法:

import java.lang.reflect.Proxy;

public class JDKDynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxyHandler proxyHandler = new ProxyHandler(realSubject);

        // 创建代理对象
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                proxyHandler);

        // 调用代理对象的方法
        proxySubject.doSomething();
    }
}

在上面的示例中,我们创建了一个RealSubject对象和一个ProxyHandler对象。然后,使用Proxy.newProxyInstance()方法创建代理对象,传入目标类的类加载器、目标类实现的接口和代理类的实例。通过代理对象调用方法时,实际上是调用了代理类的invoke()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用JDK动态代理的示例,通过代理类和InvocationHandler接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

3. CGLIB动态代理

首先,确保在项目中添加CGLIB库的依赖。

然后,定义一个目标类:

// 目标类
class RealSubject {
    public void doSomething() {
        System.out.println("RealSubject is doing something.");
    }
}

接下来,创建一个实现MethodInterceptor接口的代理类:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 代理类
class ProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Proxy is doing something before RealSubject.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("Proxy is doing something after RealSubject.");
        return result;
    }
}

在代理类的intercept()方法中,我们可以在调用目标方法之前和之后执行额外的逻辑。在CGLIB动态代理中,通过MethodProxy对象来调用目标方法。

最后,创建代理对象并调用方法:

import net.sf.cglib.proxy.Enhancer;

public class CGLIBDynamicProxyExample {
    public static void main(String[] args) {
        ProxyInterceptor proxyInterceptor = new ProxyInterceptor();

        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(proxyInterceptor);

        // 创建代理对象
        RealSubject proxySubject = (RealSubject) enhancer.create();

        // 调用代理对象的方法
        proxySubject.doSomething();
    }
}

在上面的示例中,我们创建了一个ProxyInterceptor对象,并使用Enhancer类来创建代理对象。通过setSuperclass()方法指定目标类,通过setCallback()方法指定代理类。最后,调用create()方法创建代理对象。

通过代理对象调用方法时,实际上是调用了代理类的intercept()方法,在其中执行了额外的逻辑,并最终调用目标对象的方法。

这就是一个简单的使用CGLIB动态代理的示例,通过代理类和MethodInterceptor接口,我们可以在运行时动态地生成代理对象,并在调用目标方法之前和之后执行额外的逻辑。

扩展

CGLIB在实现动态代理时使用了ASM库。ASM是一个Java字节码操作和分析框架,可以用于在运行时生成或修改字节码。CGLIB利用ASM库来生成代理类的字节码,以实现对目标类的动态代理。

在CGLIB中,Enhancer类使用了ASM库来生成代理类的字节码。ASM提供了一套API,可以直接操作字节码,包括创建类、添加字段、添加方法、修改方法等。CGLIB利用ASM的API来生成代理类的字节码,并将其加载到JVM中。

通过使用ASM库,CGLIB能够在运行时生成代理类的字节码,而无需依赖目标类的接口。这使得CGLIB可以代理那些没有实现接口的类,提供了更大的灵活性。


http://www.niftyadmin.cn/n/5029541.html

相关文章

ELK之Logstash启动异常:Logstash could not be started because there is already...

Logstash启动异常: Logstash could not be started because there is already another instance using the configured data directory. If you wish to run multiple instances, you must change the "path.data" setting. 提示我们已经有一个实例在用da…

Digger PRO - Voxel enhanced terrains

资源链接在文末 Digger PRO​​​ 是一个简单但强大的工具,可以直接从 Unity 编辑器或游戏中创建天然洞穴和悬岩。会让你感觉自己手中握有一个体素地形,且毫无瑕疵。它实际上保持着最新、最快且可靠的 Unity 地形系统,并在你需要的地方无缝创建洞穴/悬岩峭壁网格。Digger 内…

直接插入排序(C++实现)

文章目录 1. 基础概念🍑 内部排序和外部排序 2. 直接插入排序3. 动图演示4. 代码实现5. 性能分析 无论是日常生活还是很多科学领域当中,排序都是会经常面对的问题,比如按成绩对学校的学生排序,按薪水多少对公司员工排序等。 根据…

stm32---定时器输入捕获

一、输入捕获介绍 在定时器中断实验章节中我们介绍了通用定时器具有多种功能,输入捕获就是其中一种。 STM32F1除了基本定时器TIM6和TIM7,其他定时器都具有输入捕获功能 。输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获&#xf…

docker day05

昨日内容回顾: - dockerfile的优化 - 编译速度 - 充分利用缓存镜像,将不常变更的指令放在靠前的位置; - 在不影响功能的前提下,最好是可以合并多条指令,可以减少中间容器或者镜像的产生; - …

使用Java登录校验

会话技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话可以包含多次请求和响应。 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是…

JDK13特性

文章目录 JAVA13概述语法层面特性switch表达式(预览)文本块(预览) API层次特性重新实现旧版套接字API 其他变化ZGC取消未使用的内存增加废弃和移除增加项移除项废弃项 JAVA13概述 2019年9月17日,国际知名的OpenJDK开源社区发布了Java编程语言环境的最新版本OpenJDK…

【业务功能篇108】CDN Nginx

CDN(内容分发网络): CDN是一种分布式网络架构,通过将内容存储在多个地理位置的服务器上,以降低用户访问这些内容的延迟时间。这些服务器通常分布在全球各个地点,可以更快地向用户提供内容,减少了…