千家信息网

什么是JDK动态代理

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容主要讲解"什么是JDK动态代理",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"什么是JDK动态代理"吧!JDK动态代理是指:代理类实例在程序运行时,
千家信息网最后更新 2026年11月03日什么是JDK动态代理

本篇内容主要讲解"什么是JDK动态代理",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"什么是JDK动态代理"吧!

JDK动态代理是指:代理类实例在程序运行时,由JVM根据反射机制动态的生成。也就是说代理类不是用户自己定义的,而是由JVM生成的。

由于其原理是通过Java反射机制实现的,所以在学习前,要对反射机制有一定的了解。传送门:Java反射机制:跟着代码学反射

下面是本篇讲述内容:

1. JDK动态代理的核心类

JDK动态代理有两大核心类,它们都在Java的反射包下(java.lang.reflect),分别为InvocationHandler接口和Proxy类。

1.1 InvocationHandler接口

代理实例的调用处理器需要实现InvocationHandler接口,并且每个代理实例都有一个关联的调用处理器。当一个方法在代理实例上被调用时,这个方法调用将被编码并分派到其调用处理器的invoke方法上。

也就是说,我们创建的每一个代理实例都要有一个关联的InvocationHandler,并且在调用代理实例的方法时,会被转到InvocationHandler的invoke方法上。

public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable;

该invoke方法的作用是:处理代理实例上的方法调用并返回结果。

其有三个参数,分别为:

  • proxy:是调用该方法的代理实例。

  • method:是在代理实例上调用的接口方法对应的Method实例。

  • args:一个Object数组,是在代理实例上的方法调用中传递的参数值。如果接口方法为无参,则该值为null。

其返回值为:调用代理实例上的方法的返回值。

1.2 Proxy类

Proxy类提供了创建动态代理类及其实例的静态方法,该类也是动态代理类的超类。

代理类具有以下属性:

  • 代理类的名称以 "$Proxy" 开头,后面跟着一个数字序号。

  • 代理类继承了Proxy类。

  • 代理类实现了创建时指定的接口(JDK动态代理是面向接口的)。

  • 每个代理类都有一个公共构造函数,它接受一个参数,即接口InvocationHandler的实现,用于设置代理实例的调用处理器。

Proxy提供了两个静态方法,用于获取代理对象。

1.2.1 getProxyClass

用于获取代理类的Class对象,再通过调用构造函数创建代理实例。

public static Class getProxyClass(ClassLoader loader,                                         Class... interfaces)        throws IllegalArgumentException

该方法有两个参数:

  • loader:为类加载器。

  • intefaces:为接口的Class对象数组。

返回值为动态代理类的Class对象。

1.2.2 newProxyInstance

用于创建一个代理实例。

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

该方法有三个参数:

  • loader:为类加载器。

  • interfaces:为接口的Class对象数组。

  • h:指定的调用处理器。

返回值为指定接口的代理类的实例。

1.3 小结

Proxy类主要用来获取动态代理对象,InvocationHandler接口主要用于方法调用的约束与增强。

2. 获取代理实例的代码示例

上一章中已经介绍了获取代理实例的两个静态方法,现在通过代码示例来演示具体实现。

2.1 创建目标接口及其实现类

JDK动态代理是基于接口的,我们创建一个接口及其实现类。

Foo接口:

public interface Foo {    String ping(String name);}

Foo接口的实现类RealFoo:

public class RealFoo implements Foo {    @Override    public String ping(String name) {        System.out.println("ping");        return "pong";    }}

2.2 创建一个InvocationHandler

创建一个InvocationHandler接口的实现类MyInvocationHandler。该类的构造方法参数为要代理的目标对象。

invoke方法中的三个参数上面已经介绍过,通过调用method的invoke方法来完成方法的调用。

这里一时看不懂没关系,后面源码解析章节会进行剖析。

public class MyInvocationHandler implements InvocationHandler {    // 目标对象    private final Object target;    public MyInvocationHandler(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("proxy - " + proxy.getClass());        System.out.println("method - " + method);        System.out.println("args - " + Arrays.toString(args));        return method.invoke(target, args);    }}

2.3 方式一:通过getProxyClass方法获取代理实例

具体实现步骤如下:

  1. 根据类加载器和接口数组获取代理类的Class对象

  2. 过Class对象的构造器创建一个实例(代理类的实例)

  3. 将代理实例强转成目标接口Foo(因为代理类实现了目标接口,所以可以强转)。

  4. 最后使用代理进行方法调用。

@Testpublic void test1() throws Exception {    Foo foo = new RealFoo();    // 根据类加载器和接口数组获取代理类的Class对象    Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);    // 通过Class对象的构造器创建一个实例(代理类的实例)    Foo fooProxy = (Foo) proxyClass.getConstructor(InvocationHandler.class)        .newInstance(new MyInvocationHandler(foo));    // 调用 ping 方法,并输出返回值    String value = fooProxy.ping("杨过");    System.out.println(value);}

输出结果:

proxy - class com.sun.proxy.$Proxy4method - public abstract java.lang.String io.github.gozhuyinglong.proxy.Foo.ping(java.lang.String)args - [杨过]pingpong

通过输出结果可以看出:

  • 代理类的名称是以$Proxy开头的。

  • 方法实例为代理类调用的方法。

  • 参数为代理类调用方法时传的参数。

2.4 方式二:通过newProxyInstance方法获取代理实例

通过这种方法是最简单的,也是推荐使用的,通过该方法可以直接获取代理对象。

注:其实该方法后台实现实际与上面使用getProxyClass方法的过程一样。

@Testpublic void test2() {    Foo foo = new RealFoo();    // 通过类加载器、接口数组和调用处理器,创建代理类的实例    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),                                                new Class[]{Foo.class},                                                new MyInvocationHandler(foo));    String value = fooProxy.ping("小龙女");    System.out.println(value);}

2.5 通过Lambda表达式简化实现

其实InvocationHander接口也不用创建一个实现类,可以使用Lambad表达式进行简化的实现,如下代码:

@Testpublic void test3() {    Foo foo = new RealFoo();    Foo fooProxy = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),                                                new Class[]{Foo.class},                                                (proxy, method, args) -> method.invoke(foo, args));    String value = fooProxy.ping("雕兄");    System.out.println(value);}

3. 源码解析

3.1 代理类$Proxy是什么样子

JVM为我们自动生成的代理类到底是什么样子的呢?下面我们先来生成一下,再来看里面的构造。

3.1.1 生成$Proxy的.class文件

JVM默认不创建该.class文件,需要增加一个启动参数: -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

在IDEA中点击【Edit Configurations...】,打开 Run/Debug Configurations 配置框。

将上面启动参数加到【VM options】中,点击【OK】即可。

再次运行代码,会在项目中的【com.sun.proxy】目录中找到这个.class文件,我这里是"$Proxy4.class"

3.1.2 为什么加上这段启动参数就能生成$Proxy的字节码文件

在Proxy类中有个ProxyClassFactory静态内部类,该类主要作用就是生成静态代理的。

其中有一段代码ProxyGenerator.generateProxyClass用来生成代理类的.class文件。

其中变量saveGeneratedFiles便是引用了此启动参数的值。将该启动参数配置为true会生成.class文件。

3.1.3 这个代理类$Proxy到底是什么样子呢

神秘的面纱即将揭露,前面很多未解之迷在这里可以找到答案!

打开这个$Proxy文件,我这里生成的是$Proxy4,下面是内容:

// 该类为final类,其继承了Proxy类,并实现了被代理接口Foopublic final class $Proxy4 extends Proxy implements Foo {    // 这4个Method实例,代表了本类实现的4个方法    private static Method m1;    private static Method m2;    private static Method m3;    private static Method m0;    // 静态代码块根据反射获取这4个方法的Method实例    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.github.gozhuyinglong.proxy.Foo").getMethod("ping");            m0 = Class.forName("java.lang.Object").getMethod("hashCode");        } catch (NoSuchMethodException var2) {            throw new NoSuchMethodError(var2.getMessage());        } catch (ClassNotFoundException var3) {            throw new NoClassDefFoundError(var3.getMessage());        }    }    // 一个公开的构造函数,参数为指定的 InvocationHandler     public $Proxy4(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);        }    }    // Foo接口的实现方法,最终调用了 InvocationHandler 中的 invoke 方法    public final String ping(String var1) throws  {        try {            return (String)super.h.invoke(this, m3, new Object[]{var1});        } catch (RuntimeException | Error var3) {            throw var3;        } catch (Throwable var4) {            throw new UndeclaredThrowableException(var4);        }    }    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);        }    }}

通过该文件可以看出:

  • 代理类继承了Proxy类,其主要目的是为了传递InvocationHandler。

  • 代理类实现了被代理的接口Foo,这也是为什么代理类可以直接强转成接口的原因。

  • 有一个公开的构造函数,参数为指定的InvocationHandler,并将参数传递到父类Proxy中。

  • 每一个实现的方法,都会调用InvocationHandler中的invoke方法,并将代理类本身、Method实例、入参三个参数进行传递。这也是为什么调用代理类中的方法时,总会分派到InvocationHandler中的invoke方法的原因。

3.2 代理类是如何创建的

我们从Proxy类为我们提供的两个静态方法开始getProxyClass和newProxyInstance。上面已经介绍了,这两个方法是用来创建代理类及其实例的,下面来看源码。

3.2.1 getProxyClass 和 newProxyInstance方法

通过上面源码可以看出,这两个方法最终都会调用getProxyClass0方法来生成代理类的Class对象。只不过newProxyInstance方法为我们创建好了代理实例,而getProxyClass方法需要我们自己创建代理实例。

3.2.2 getProxyClass0 方法

下面来看这个统一的入口:getProxyClass0

从源码和注解可以看出:

  • 代理接口的最多不能超过65535个

  • 会先从缓存中获取代理类,则没有再通过ProxyClassFactory创建代理类。(代理类会被缓存一段时间。)

3.2.3 WeakCache类

这里简单介绍一下WeakCache 类,该类主要是为代理类进行缓存的。获取代理类时,会首先从缓存中获取,若没有会调用ProxyClassFactory类进行创建,创建好后会进行缓存。

3.2.4 ProxyClassFactory类

ProxyClassFactory是Proxy类的一个静态内部类,该类用于生成代理类。下图是源码的部分内容:

  • 代理类的名称就是在这里定义的,其前缀是$Proxy,后缀是一个数字。

  • 调用ProxyGenerator.generateProxyClass来生成指定的代理类。

  • defineClass0方法是一个native方法,负责字节码加载的实现,并返回对应的Class对象。

3.3 原理图

为了便于记录,将代理类的生成过程整理成了一张图。

到此,相信大家对"什么是JDK动态代理"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

代理 方法 实例 接口 参数 对象 生成 动态 文件 静态 代码 反射 处理 两个 处理器 数组 源码 内容 目标 缓存 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 科技平台互联网 互联网科技与金融科技 科技创新互联网企业 汇久创客互联网科技 亿人帮互联网科技 互联网公司会议桌创意科技软装饰 湖南洣水互联网科技有限公司扣款 互联网和高科技公司 济南华为互联网科技产业总部基地 互联网经济与金融科技 赶超互联网科技 微信多开 未来五年的互联网科技 玉溪新华互联网科技好不好 每天都有哪些好的互联网科技 中天科技中国工业互联网 银川新华互联网科技学校原画 北京天晋互联网科技有限公司 互联网科技博主张洲 亚光科技低轨卫星互联网 前海云享互联网科技 手磨咖啡 重生之互联网科技帝国 朗新科技与工业互联网 互联网最新的科技有限公司 奇酷互联网络科技公司 团飞科技与伯堂互联网服务平台 互联网在线教育科技创业 南通创西澳互联网科技有限公司 北京互联网科技类企业分布 山东蛋芒互联网科技有限公司
0