JDK动态代理:深入解析Java动态代理的核心实现

发布于:2025-07-12 ⋅ 阅读:(17) ⋅ 点赞:(0)

摘要:本文深入剖析JDK动态代理的实现机制,结合字节码生成与调用转发原理,揭示其如何成为Spring等框架的AOP基石。

前面看Spring AOP的源码,所以对于 JDK 动态代理具体是如何实现的产生了兴趣,想着从源码上了解这个原理的话,也有助于对 spring-aop 模块的理解;


JDK 动态代理其实是 Java 标准库提供的基于接口的代理实现,其核心原理可概括为 运行时动态生成代理类字节码 + 方法调用转发。以下是详细解析:

一、动态代理的本质

动态代理是一种运行时生成代理对象的技术,无需手动编写代理类。JDK动态代理是Java标准库提供的原生方案,其核心能力:

  • 接口代理:仅支持基于接口的代理
  • 无侵入扩展:在方法调用前后插入自定义逻辑(如日志、事务)
  • 运行时字节码生成:动态创建代理类的.class文件

🌟 典型场景:Spring的@Transactional事务管理、MyBatis的Mapper接口实现

二、核心实现机制

1. 核心类与职责

类/接口 职责
java.lang.reflect.Proxy 入口类,提供newProxyInstance()创建代理对象
InvocationHandler 调用处理器,所有代理方法调用均转发到其invoke()方法
ProxyGenerator (内部) 动态生成代理类字节码(JDK内部类)

2. 代理对象创建流程

// Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
// 创建代理对象的核心代码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
     // 1. 动态生成代理类字节码
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

		// 2. 获取构造器并实例化
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

关键步骤:

  1. 根据接口数组生成代理类字节码(如$Proxy0.class
  2. 通过类加载器加载字节码
  3. 反射构造代理对象,绑定InvocationHandler

三、完整实践示例解析

一般会使用实现了 InvocationHandler接口 的类作为代理对象的生产工厂类,并且通过持有被代理对象target,来在 invoke() 方法中对被代理对象的目标方法进行增强和调用,这些通过下面这段代码就看懂:

// 我的代理工厂类,实现了InvocationHandler 接口
public class MyProxyFactory implements InvocationHandler {
    private Object target = null;

    public Object getInstance(Object target) {

        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        System.out.println("前置增强");
        res = method.invoke(target, args);
        System.out.println("后置增强");

        return res;
    }
}

但代理对象是如何生成的?invoke()方法 又是如何被调用的呢?

带着这两个疑问接着看下面的代码:

// 定义一个 test 的接口
public interface MyInterface {
    void play();
}

/**
 * 基于JDK代理的标准,该类实现了接口 MyInterface 和接口的 play() 方法,可以作为被代理类
 */
public class TargetObject implements MyInterface{
    @Override
    public void play() {
        System.out.println("TargetObject play");
    }
}

基于上面的可以杯代理的TargetObject 类, 来做下测试,代码如下:

public class ProxyTest {

    public static void main(String[] args) {
        TargetObject target = new TargetObject();
        /**
         * MyProxyFactory 实现了 InvocationHandler接口,其中的 getInstance()方法就是利用Proxy类生成并返回了target目标对象的代理对象;
         * MyProxyFactory 持有对 target 的引用,可以在invoke()中对目标方法前置后置做增强处理,并完成对target相应方法的调用。
         */
        MyProxyFactory proxyFactory = new MyProxyFactory();
        /**
         * 这个mi就是JDK生成的代理类,即动态生成的代理类$Proxy0的实例,该实例中的方法都持有对invoke()方法的回调,
         * 所以当调用其方法时,就能够执行invoke()中的增强处理
         */
        MyInterface mi = (MyInterface) proxyFactory.getInstance(target);
        // 可以看到 mi 的 Class 到底是什么
        System.out.println(mi.getClass());
        /**
         * 这里实际上调用的就是$Proxy0代理类实例对象中对play()方法的实现,
         * 结合下面的代码可以看到play()方法 通过 super.h.invoke() 完成了对InvocationHandler对象(proxyFactory)中invoke()方法 的回调,
         * 所以我们才能够通过 invoke()方法实现对 target对象方法的前置后置增强处理
         */
        mi.play();
        // 总的来说,JDK动态生成的代理类中对 invoke()方法进行了回调,就是在 invoke()方法中完成target目标方法的调用,及前置后置增强,
    }
}

// 运行结果:
class com.sun.proxy.$Proxy0
前置增强
TargetObject play
后置增强

由上面的分析我们有的大致了解到实际生产的代理对象类是class com.sun.proxy.$Proxy0;因为代理类是运行时生成的,运行结束也就看不到了,为了我们方便的看生成的代理类究竟是长啥样, 我们可以借助 JDK 提供的工具类(ProxyGenerator)的 generateProxyClass 方法生成代理类的字节码,并保存为 .class文件。具体实现如下代码:

public class ProxyTest {

    @Test
    public void generatorSrc() {
        // 调用 generateProxyClass 方法生成字节码
        byte[] bytesFile = ProxyGenerator.generateProxyClass("$Proxy0", TargetObject.class.getInterfaces());
        // 将字节码写入文件
        String path = System.getProperty("user.dir") + "\\$Proxy0.class";
        File file = new File(path);
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(bytesFile);
            fos.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

接下来我们可以借助Java反编译工具, 或者直接用idea打开$Proxy0.class文件一探究竟。

四、代理类结构解析

打开的$Proxy0.class结构如下:

// 生成的代理类示例(反编译后)
// Proxy 生成的代理类,可以看到,其继承了 Proxy,并且实现了 被代理类的接口MyInterface
public final class $Proxy0 extends Proxy implements MyInterface {
	// 静态方法引用表
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    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 play() throws  {
        try {
        	// 关键调用:转发至InvocationHandler
        	// 这个 h 其实就是我们调用 Proxy.newProxyInstance()方法 时传进去的 MyProxyFactory对象(它实现了InvocationHandler接口),
        	// 该对象的 invoke()方法 中实现了对目标对象的目标方法的增强。
        	// 看到这里,利用动态代理实现方法增强的实现原理就很清晰了
            super.h.invoke(this, m3, (Object[])null);
        } 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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            // 实例化 MyInterface 的 play()方法
            m3 = Class.forName("com.umzhang.springwebdemo.testproxy.MyInterface").getMethod("play");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

核心设计:

  • 继承Proxy父类,持有InvocationHandler实例
  • 静态块初始化接口方法引用
  • 每个代理方法直接调用InvocationHandler.invoke()

四、方法调用全流程

在这里插入图片描述
关键环节:

  1. 代理方法拦截:代理类方法被调用
  2. 统一转发:调用InvocationHandler.invoke()
  3. 反射执行:通过method.invoke(target, args)调用目标方法
  4. 增强扩展点:在invoke()中可插入前后逻辑

六、关键特性与限制

✅ 核心优势:

  • 动态字节码生成,使用ProxyGenerator动态生成.class文件
  • 类名格式:$ProxyN(N为自增数字)

⚠️ 使用限制

  • 接口依赖:目标类必须实现至少一个接口(无法代理无接口的类)
  • final方法:不能代理final方法(代理类通过继承实现,无法重写final方法)
  • 私有方法:只能代理接口声明的方法(私有方法不会被拦截)

与CGLIB代理对比
在这里插入图片描述

总结

JDK动态代理通过运行时字节码生成 + 方法调用转发两大核心机制,实现了优雅的代理模式:

  1. 技术价值:解耦业务逻辑与横切关注点(如日志、事务);为Spring等框架提供AOP底层支持。
  2. 应用边界:适用于基于接口的代理场景;性能敏感场景需关注反射调用开销(可结合JIT优化)。

JDK 动态代理通过 运行时生成代理类字节码 + 统一方法转发到 InvocationHandler 实现动态代理。其设计优雅地结合了 Java 反射机制与类加载机制,成为 Java 生态中 AOP 和中间件开发的基础技术。尽管存在接口依赖的限制,但在 Spring 等框架的巧妙封装下,仍被广泛应用于企业级开发。
掌握其原理,能更深入理解Java生态中 AOP 编程、RPC 框架、Spring 容器、Mock 测试 等技术的实现本质。


End!


网站公告

今日签到

点亮在社区的每一天
去签到