JDK动态代理:深入解析Java动态代理的核心实现
文章目录
- 一、动态代理的本质
- 二、核心实现机制
- 1. 核心类与职责
- 2. 代理对象创建流程
- 三、完整实践示例解析
- 四、代理类结构解析
- 四、方法调用全流程
- 六、关键特性与限制
- 总结
摘要:本文深入剖析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);}
}
关键步骤:
- 根据接口数组生成代理类字节码(如
$Proxy0.class
) - 通过类加载器加载字节码
- 反射构造代理对象,绑定
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);}@Overridepublic 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{@Overridepublic 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 {@Testpublic 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()
四、方法调用全流程
关键环节:
- 代理方法拦截:代理类方法被调用
- 统一转发:调用
InvocationHandler.invoke()
- 反射执行:通过
method.invoke(target, args)
调用目标方法 - 增强扩展点:在
invoke()
中可插入前后逻辑
六、关键特性与限制
✅ 核心优势:
- 动态字节码生成,使用
ProxyGenerator
动态生成.class
文件 - 类名格式:
$ProxyN
(N为自增数字)
⚠️ 使用限制
- 接口依赖:目标类必须实现至少一个接口(无法代理无接口的类)
- final方法:不能代理final方法(代理类通过继承实现,无法重写final方法)
- 私有方法:只能代理接口声明的方法(私有方法不会被拦截)
与CGLIB代理对比:
总结
JDK动态代理通过运行时字节码生成 + 方法调用转发两大核心机制,实现了优雅的代理模式:
- 技术价值:解耦业务逻辑与横切关注点(如日志、事务);为Spring等框架提供AOP底层支持。
- 应用边界:适用于基于接口的代理场景;性能敏感场景需关注反射调用开销(可结合JIT优化)。
JDK 动态代理通过 运行时生成代理类字节码 + 统一方法转发到
InvocationHandler
实现动态代理。其设计优雅地结合了 Java 反射机制与类加载机制,成为 Java 生态中 AOP 和中间件开发的基础技术。尽管存在接口依赖的限制,但在 Spring 等框架的巧妙封装下,仍被广泛应用于企业级开发。
掌握其原理,能更深入理解Java生态中 AOP 编程、RPC 框架、Spring 容器、Mock 测试 等技术的实现本质。
End!