静态代理及JDK动态代理原理说明

本文深入探讨Java动态代理,尤其是JDK动态代理。通过实例对比静态代理和动态代理,解释动态代理在运行时生成代理类的过程,并分析JDK动态代理的源码,揭示其内部如何生成字节码文件并加载到内存中。最后,通过源码分析解答了静态代理与动态代理的本质区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式,对于Java开发人员来说是一个必须掌握的知识点,但是大部分时候我们只需要知道代理模式的定义和使用方式即可,作者原来也是这样认为的,但是随着工作经历的增加,发现在各种优秀的框架中都对代理模式进行大量的运用,这使作者意识到需要仔细的研究下代理模式。本文将主要对JDK动态代理和静态代理进行举例说明,

代理模式说明

代理模式主要分为,静态代理和动态代理,而动态代理又可以区分为JDK动态代理和CGlib两种方式,本文主要讨论动态代理中的JDK动态代理,希望初学者读者不要混淆。

关于静态代理和动态代理区别的疑问

我们常说静态代理、动态代理,可是什么才算是静态代理什么才算是动态代理呢?这是我在学习JavaEE阶段的一个问题,每次被问到就只能回答基本概念:“静态代理就是我们编写的代理,而动态代理就是动态生成的代理”,如果按照概念理解,那么静态代理比较好懂,就是我们自己写一个中间类去代理目标类,类似于卖房中介,可是动态代理呢?动态生成的不也是我们通过使用JDK提供的类Proxy来达到代理的目的吗?,不也是我们自己写的“静态代码”吗?
这个问题困扰了我很久,如果有这个疑问的小伙伴通过这篇文章因该可以解决这个疑惑。
下面是功能完全相同的业务代码,分别使用静态代理和动态代理编写

举例静态代理

//测试方法
public class StaticProxyTest {
    public static void main(String[] args) {
        System.out.println("我想买房...");
        System.out.println("找个中介");
        //找个中介买房
        MaiFang maiFang = new ZhongJie();
        //买房
        maiFang.sell();
    }
}
//中介类实现卖方接口
class ZhongJie implements MaiFang{
//要买房首先要有一个卖房的房东
    FangDong fangdong  = new FangDong();
    @Override
    public void sell() {
        System.out.println("中介联系房东");
        //房东卖房
        fangdong.sell();
        System.out.println("中介收取手续费");
    }
}
//房东类实现卖房接口
class FangDong implements MaiFang{
    @Override
    public void sell() {
        System.out.println("房东卖房");
    }
}
//不管是房东还是中介都应该能卖房
interface MaiFang{
    void sell();
}
==输出结果:==
我想买房...
找个中介
中介联系房东
房东卖房
中介收取手续费

举例JDK动态代理

//动态代理测试类
public class DynamicProxyTest {
    public static void main(String[] args) throws Exception {
    //动态房东要卖房
        FangDong fangDong = new FangDong();
        //动态代理的执行卖房逻辑
        InvocationHandler invocationHandler = new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("中介联系房东");
                //执行(绑定)卖方方法
                method.invoke(fangDong,args);
                System.out.println("中介收取手续费");
            }
        };
        System.out.println("我想买房...");
        System.out.println("找个中介");
        //动态卖房
        MaiFang maiFang = (MaiFang)Proxy.newProxyInstance(FangDong.class.getClassLoader(), new Class[]{MaiFang.class}, invocationHandler);
        maiFang.sell();
    }
}
//类比静态房东
class FangDong implements MaiFang{
    @Override
    public void sell() {
        System.out.println("房东卖房");
    }
}
//不管是房东还是中介都应该能卖房
interface MaiFang{
    void sell();
}
==输出结果:==
找个中介
中介联系房东
房东卖房
中介收取手续费

直接看两种代理方式不能区分上面提出的问题,我们需要进一步分析,下面开始分析JDK动态代理源码

JDK动态代理源码

由于有些源码不利于我们分析,所以作者这里只抽取核心逻辑进行说明
== 源码 ==

newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
    {
        /*
         * **1.核心** 该方法返回了动态生成的代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
        *  根据动态生成的代理类反射获取构造器
        */
        final Constructor<?> cons = cl.getConstructor(constructorParams);
         /*
        *  通过反射生成的代理类构造器构建代理对象,业务上理解为中介对象
        */
        return cons.newInstance(new Object[]{h});
    }
    private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        // 官方注释:如果由实现给定接口的给定加载器定义的代理类存在,这将简单地返回缓存副本;否则,它将通过 ProxyClassFactory 创建代理类
        /*
         * **2.核心** 动态生成的代理类会存储在内存(缓存)中
         */
        return proxyClassCache.get(loader, interfaces);
    }
	public V get(K key, P parameter) {
        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        //官方注释:创建 subKey 并从 valuesMap 中检索该 subKey 存储的可能的 Supplier<V>
        /*
         * **3.核心** subKeyFactory.apply()创建代理对象 这里进入是一个接口,需要进入其实现类,ProxyClassFactory代理类工厂 这个类很重要,所以粘贴整个类
         */
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    }
    /**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     * 一个工厂函数,它生成、定义和返回给定 ClassLoader 和接口数组的代理类
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        //所有代理类名称的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // next number to use for generation of unique proxy class names
        //用于生成唯一代理类名称的下一个数字
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            /*
             * Choose a name for the proxy class to generate.
             * 为要生成的代理类选择一个名称原子累加获取0.1.2.3.....
             */
            long num = nextUniqueNumber.getAndIncrement();
            //之后就会生成一个$Proxy0字符串
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 生成指定的代理类
             * **4.核心** 生成代理类的字节码文件(二进制)这里就是核心之后会贴出来生成的字节码文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            /*
             * **5.核心** 根据内存中的字节码文件生成代理类 并返回出去
             */
            return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
               
        }
    }

== 动态生成的字节码代理类(以业务-卖房为例) ==

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

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

final class $Proxy0 extends Proxy implements MaiFang {
	//生成的静态的方法类 
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
	//生成在文件最底端吗,为了方便阅读拿到这个位置
	static {
        try {
        //反射获得方法对象
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("io.test.MaiFang").getMethod("sell");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
	//所需要代理的卖房方法
    public final void sell() throws  {
        try {
        //调用代理对象的方法
            super.h.invoke(this, m3, (Object[])null);
        //变换之后
        // Proxy.InvocationHandler.invoke("代理类","代理类的sell()",...)

其实就是动态代理的invoke方法:如下图
在这里插入图片描述

        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

可以对比下生成的字节码文件和静态代理类,可以发现其实就是“中介类”只不过名称是系统自动生成的,可以类比我们静态代理的“zhongjie”类

总结

通过上面的源码分析,想必小伙伴们应该能收获开头问题的答案了吧,其实我们可以发现动态代理也是会生像静态代理那样生成代理类的,只不过是在程序运行的时候自动创建的,所以静态代理和动态代理的区分其实就是这个代理类生成的过程,由我们编写出来的代理类在编译阶段形成字节码文件的就是静态代理,由系统在运行阶段自动生成的$proxy**类(字节文件)就是动态代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值