Java动态代理其实很简单

简介: 在使用Java 动态代理时,一直很迷惑,什么是动态代理,动态在了那里?它和静态代理的区别是什么?

在使用Java 动态代理时,一直很迷惑,什么是动态代理,动态在了那里?它和静态代理的区别是什么?但是很遗憾,没有找到一个能真正简单明了的告诉我原因的博客,于是决定自己动手,分析一下。


首先,本篇的主要围绕点如下:


当然,对于其中的具体实现,并不会太去关注,本篇主旨是简单通俗的告诉你,什么是动态代理,它的流程是什么。

首先,什么是动态代理?

我复制一个大佬的解释如下:


利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在运行时才知道具体的实现,spring aop就是此原理。

这个解释有问题吗,没有问题,很简单明了,但是对于新同学,特别是刚入行没多久的,带来的感受只有一个,那就是MMP?

拒绝官方话语,让技术变得简单通俗,这是我们每个技术人都追寻的目标吧。

好了开始我们的教程:

我们用一个简单的demo开始。

首先,这是一个接口:

public interface Subject {
    void play();
}

很简答,它的作用我们只是为了打印一句话。


接着有一个扩展类,实现了它:

public class XiaoQing implements Subject {
    @Override
    public void play() {
        System.out.println("我是小强,我要去跑步");
    }
}

最后,看我们具体的处理类

public class TestProxy implements InvocationHandler {
    private Subject subject;
    public TestProxy(Subject subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是动态代理");
        method.invoke(subject, args);
        System.out.println("结束");
        return null;
    }
    public static void main(String[] args) {
        XiaoQing xiaoQing = new XiaoQing();
        TestProxy testProxy = new TestProxy(xiaoQing);
        //拿到代理对象
        Subject subject = (Subject) Proxy.newProxyInstance(xiaoQing.getClass().getClassLoader(), xiaoQing.getClass().getInterfaces(), testProxy);
        subject.play();
    }
}

先放下main方法不看,我们关注 TestProxy 这个类,它实现了一个 InvocationHandler 接口,并实现了 invoke 方法。

我们看一下 invoke方法:


熟悉反射的同学应该会发现,这里我们调用了 method.invoke()方法,并传入了我们的接口对象和参数args.从而调用我们的接口中的方法。也就是说从这里我们可以插入相应的执行前后方法。

继续我们的流程:

我们看一下main方法,也就是我们的测试入口:

里面也很简单,我们主要关注 Proxy 这个类。

public class Proxy implements java.io.Serializable {
    。。。
    protected InvocationHandler h;
   。。。

可以发现,它内部持有一个 InInvocationHandler 接口对象,那这个对象是干啥的呢,我们点进去看一下:

很简单,里面只有一个方法,invoke,也就是俗称的拦截方法

interface InvocationHandler{
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
 InvocationHandler 是由代理实例的<i>调用处理程序</ i>实现的接口。 * * <p>每个代理实例都有一个关联的调用处理程序。 在代理实例上调用某个方法时,该方法的调用将被编码并调度到其调用处理程序的{@code invoke} *方法

简单粗暴,也就是说我们每个代理类都有一个关联的调用处理类,而这个接口的作用就是将我们调用的代理方法调度到真实调用处去。也就是调用它的 invoke 方法。


可能听得有点绕,没关系,我们先暂且持有这个疑问,去看一下 Proxy.newProxyInstance方法:

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {

有了上面的解析,大家可能还会存在疑惑,那下面我们去看一下动态代理生成的代理类,一探究竟:

这里碍于原因,我们从网上找一段生成的代理类,来看一下,差别也不会很大:

。。。
public final class $Proxy0
extends Proxy
implements HelloService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke((Object)this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }
    public final String sayHello(String string) {
        try {
            return (String)this.h.invoke((Object)this, m3, new Object[]{string});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }
   。。。
    }
}

我们看一下sayHello 方法,我们先将 sayHello 当做我们刚才接口中的 play方法,再观察内部实现:

其中 h 就是invocationHandler,当我们在调用接口play方法时,其实代理类,通过 invocationHandler对象 调用 invoke 方法并传入代理对象,方法,及我们调用时传递过来的参数,然后 定位到我们真实要调用的方法,从而我们可以在调用方法的前后就可以插入我们相应的代码实现动态代理。

最后,我们总结一下我们最开始时思维导图中的几个问题:

InvocationHandler ?为什么要实现它?

InvocationHandler 就相当于一个委托处理,我们实现它的 invoke 方法,然后便于我们可以动态的在 invoke 中插入自己的代码,实现相应逻辑。

Proxy.newProxyInstance?

Proxy.newProxyInstance 的主要作用就是通过反射生成我们的代理对象,从而将我们的代理对象关联到传入的 InvocationHandler对象上。

Invoke?

便于我们动态的插入自己的代码,并为了便于 我们生成的代理对象调用接口方法时,最后实则调用 invoke方法,并传入代理对象,真实方法和对应的参数。

所以所谓的动态代理,实则是通过类加载器在初始化时动态生成了一个代理对象,这个代理对象实现了我们接口的所有方法,并在后续我们调用时,内部通过 invocationHandler 对象调用invoke方法从而定位到我们真实的调用位置,从而让我们可以在调用前后插入我们相应的代码。

上面只是我个人的理解,如果有什么地方有问题,也欢迎大家提出。


目录
相关文章
|
28天前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
2月前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
32 0
[Java]静态代理与动态代理(基于JDK1.8)
|
2月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
91 1
|
2月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
2月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
5月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
44 2
day27:Java零基础 - 动态代理
|
4月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
284 0
|
4月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
202 0
|
5月前
|
开发框架 Java Android开发
Java中的类反射与动态代理详解
Java中的类反射与动态代理详解
|
5月前
|
Java 数据安全/隐私保护
Java中的动态代理机制详解
Java中的动态代理机制详解