Java设计模式---动态代理原理源码分析

发布时间:2026/8/18 19:17:18

Java设计模式---动态代理原理源码分析
一、前言上篇文章《Java设计模式代理模式》中介绍了两种代理模式静态代理和动态代理的应用场景和实际应用案例。本篇文章中对动态代理的原理做进行深入的分析。二、动态代理的核心疑问关于动态代理初看可能会比较费解大概有如下几个疑问代理是怎么形成的代理类在哪里TimeHandler类是做什么用的在哪里被调用了客户端调用的时候写的是调用m.move();程序是如何执行到了TimeHandler对象的invoke方法中了呢这篇文章主要针对如上几个疑问进行展开。三、动态代理的基本概念首先声明一点案例中的TimeHandler并不是代理类而是代理依赖的一个类而已。真正的代理类是JDK直接生成的字节码并进行加载的所以对用户是不可见的。生成代理类的代码是这个Moveable m (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);这里向newProxyInstance传递了三个参数分别是原始类的加载器原始类实现的接口MoveableTimeHandler类的对象。四、Proxy.newProxyInstance源码分析Proxy.newProxyInstance是动态代理的核心入口方法其源码实现如下public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) { Objects.requireNonNull(h); final Class? caller System.getSecurityManager() null ? null : Reflection.getCallerClass(); /* * Look up or generate the designated proxy class and its constructor. */ Constructor? cons getProxyConstructor(caller, loader, interfaces); return newProxyInstance(caller, cons, h); }newProxyInstance的代码实现比较清晰主要流程如下检查InvocationHandler参数不能为 null获取调用者类用于安全检查通过getProxyConstructor方法获取代理类的构造方法调用另一个重载的newProxyInstance方法创建代理实例4.1 getProxyConstructor方法解析getProxyConstructor方法负责获取或生成代理类的构造方法private static Constructor? getProxyConstructor(Class? caller, ClassLoader loader, Class?... interfaces) { // optimization for single interface if (interfaces.length 1) { Class? intf interfaces[0]; if (caller ! null) { checkProxyAccess(caller, loader, intf); } return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) - new ProxyBuilder(ld, clv.key()).build() ); } else { // interfaces cloned final Class?[] intfsArray interfaces.clone(); if (caller ! null) { checkProxyAccess(caller, loader, intfsArray); } final ListClass? intfs Arrays.asList(intfsArray); return proxyCache.sub(intfs).computeIfAbsent( loader, (ld, clv) - new ProxyBuilder(ld, clv.key()).build() ); } }在getProxyConstructor函数中可以看到 if-else 分支这里是对单接口的情况做了代码优化。我们主要关注其代理类的生成部分就是new ProxyBuilder(ld, clv.key()).build()。(ld, clv) - new ProxyBuilder(ld, clv.key()).build()这种写法是 lambda 表达式的语法非常精简。4.2 ProxyBuilder.build方法分析Constructor? build() { Class? proxyClass defineProxyClass(module, interfaces); final Constructor? cons; try { cons proxyClass.getConstructor(constructorParams); } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } AccessController.doPrivileged(new PrivilegedActionVoid() { public Void run() { cons.setAccessible(true); return null; } }); return cons; }在build方法中从语义上可以看到代理类proxyClass在defineProxyClass方法中生成。之后通过反射并根据构造函数的参数获取到代理类的构造方法并返回。这里的参数为private static final Class?[] constructorParams { InvocationHandler.class };。所以看到这里应该明白了TimeHandler的作用了就是作为代理类的构造方法的一个参数也就是代理类依赖的对象。到这里也就找到了文章开始处提出的问题二的答案。这里可以猜测一下代理类中的对应接口的方法应该是调用的TimeHandler类的invoke方法通过控制invoke的参数来调用不同的方法。4.3 defineProxyClass方法解析在defineProxyClass方法中最关键的代码如下byte[] proxyClassFile ProxyGenerator.generateProxyClass( proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags); try { Class? pc UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, null); reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE); return pc; } catch (ClassFormatError e) { /* A ClassFormatError here means that (barring bugs in the proxy class generation code) there was some other invalid aspect of the arguments supplied to the proxy class creation (such as virtual machine limitations exceeded). */ throw new IllegalArgumentException(e.toString()); }这里先生成字节码之后将其加载到内存中。4.4 ProxyGenerator.generateProxyClass方法static byte[] generateProxyClass(final String name, Class?[] interfaces, int accessFlags) { ProxyGenerator gen new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile gen.generateClassFile(); if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedActionVoid() { public Void run() { try { int i name.lastIndexOf(.); Path path; if (i 0) { Path dir Paths.get(name.substring(0, i).replace(., File.separatorChar)); Files.createDirectories(dir); path dir.resolve(name.substring(i1, name.length()) .class); } else { path Paths.get(name .class); } Files.write(path, classFile); return null; } catch (IOException e) { throw new InternalError( I/O exception saving generated file: e); } } }); } return classFile; }在generateProxyClass方法中先生成了ProxyGenerator对象然后调用对象的generateClassFile生成字节码。可以看到其中有个saveGeneratedFiles的标志位表示是否需要保存生成的字节码文件。在generateClassFile方法中创建了ProxyMethod字段表和方法表集合并将内容按照字节码的规范写入到流中。至此代理类就生成了文章开始处的问题一就回答完毕了。此处代码中可以根据语义看到会使用InvocationHandler的方法目前对 Java 的 class 文件的格式还不够了解有机会研究一下。4.5 保存生成的代理类文件刚刚说到代码中是存在打印生成的代理类的逻辑的就是saveGeneratedFiles标志位在该变量定义处可以看到private static final boolean saveGeneratedFiles java.security.AccessController.doPrivileged( new GetBooleanAction( jdk.proxy.ProxyGenerator.saveGeneratedFiles)).booleanValue();表示该变量是由jdk.proxy.ProxyGenerator.saveGeneratedFiles控制的。所以我们在代码中只需要指定这个值为 true 就可以了代码如下import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) throws Exception{ System.setProperty(jdk.proxy.ProxyGenerator.saveGeneratedFiles, true); Car car new Car(); Class? cls car.getClass(); InvocationHandler h new TimeHandler(car); Moveable m (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h); m.move(); System.out.println(); m.move_back(); System.out.println();System.out.println(); } }运行之后就可以在工程目录下找到对应的代理类的 class 文件啦。使用 IDEA 打开可以看到对应的 Java 代码如下4.6 生成的代理类源码分析// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Moveable { private static Method m1; private static Method m3; private static Method m2; private static Method m4; 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 void move() throws Exception { try { super.h.invoke(this, m3, (Object[])null); } catch (Exception | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } 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 move_back() throws Exception { try { super.h.invoke(this, m4, (Object[])null); } catch (Exception | 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)); m3 Class.forName(Moveable).getMethod(move); m2 Class.forName(java.lang.Object).getMethod(toString); m4 Class.forName(Moveable).getMethod(move_back); m0 Class.forName(java.lang.Object).getMethod(hashCode); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }4.7 代理类解读与总结代理类解读代理类的构造方法接收一个InvocationHandler对象调用父类Proxy的初始化方法使用该对象为依赖h的对象赋值。以move方法为例我们可以看到代理类实现了Moveable接口在move方法的实现中调用了InvocationHandler对象的invoke方法并且其中第二个参数为m3对象。而m3对象是静态块中通过反射获取到的Moveable接口的move方法。所以此处就回答的文章开始处的问题三。到这里动态代理的原理就非常清晰了代理类是 JDK 直接以字节码的形式生成出来的继承Proxy类实现客户端定义的接口。因为Proxy依赖InvocationHandler实现类所以代理类也同样依赖。对于接口中定义的函数move代理类中也会实现其逻辑是调用InvocationHandler类中的invoke方法并将方法move方法作为invoke的一个参数。在invoke方法中可以进行功能扩展进而执行invoke参数中的方法完成对原始对象的move方法的调用。不得不佩服 Java 源码的作者们的深厚功力能够设计出如此高扩展的结构自己需要学习和实践的还有很多。参考文章看了这篇文章非常不错转载[Java] 动态代理的实现原理 - 简书

相关新闻

从每天两小时到十分钟:三月七小助手把星穹铁道的日常变成了“托管模式“

从每天两小时到十分钟:三月七小助手把星穹铁道的日常变成了“托管模式“

2026/8/18 19:07:18

从每天两小时到十分钟:三月七小助手把星穹铁道的日常变成了"托管模式" 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 深夜十一点,…

MunAi – 人工智能作家与文案AI WordPress主题

MunAi – 人工智能作家与文案AI WordPress主题

2026/8/18 19:07:18

关于 MunAi – 人工智能作家与文案WordPress主题,它是一个为内容创作者、营销人员和博主设计的专用WordPress主题。结合搜索结果,它的特点和优点可以归纳如下: ✨ 主要特点 AI驱动的内容生成:核心功能是利用人工智能自动生成文章…

wol 实时状态监控原理剖析:SSE 流与 Ping 探测机制详解

wol 实时状态监控原理剖析:SSE 流与 Ping 探测机制详解

2026/8/18 19:07:18

wol 实时状态监控原理剖析:SSE 流与 Ping 探测机制详解 【免费下载链接】wol 🦭 Wake up your devices with a single command or click. A Wake-On-LAN tool that works via CLI and web interface. 项目地址: https://gitcode.com/gh_mirrors/wo/wol…

“AI能不能替代某一个岗位?”为什么 Agent 可能会重新定义职业

“AI能不能替代某一个岗位?”为什么 Agent 可能会重新定义职业

2026/8/18 20:07:21

一、如果软件开始主动做事如果有一天,Agent真的足够强大了,人还需要工作吗?这个问题听起来有些遥远。但当我开始真正尝试把Agent放进真实的工作场景中思考之后,我越来越觉得“AI能不能替代某一个岗位”这个担忧已经不值得放在一个…

构建跳数感知RAG智能体评测基准:从黑盒到白盒的多步推理诊断

构建跳数感知RAG智能体评测基准:从黑盒到白盒的多步推理诊断

2026/8/18 20:07:20

1. 项目概述:为什么我们需要一个“跳数感知”的RAG智能体评测基准? 最近和几个做RAG(检索增强生成)和AI智能体(Agent)的朋友聊天,大家都有一个共同的痛点:当我们的智能体需要执行多步…

全局异常处理(@ControllerAdvice)

全局异常处理(@ControllerAdvice)

2026/8/18 20:07:20

全局异常处理(ControllerAdvice) 实验目的 1.理解异常处理在后端开发中的作用。 2.掌握 Spring Boot 全局异常处理机制。 3.掌握 ControllerAdvice 和 ExceptionHandler 的使用方法。 4.能够统一处理系统异常并返回规范格式数据。 5.结合 Result 统一返…

后端参数校验

后端参数校验

2026/8/18 20:07:20

后端参数校验实验目的1.理解后端参数校验的重要性。 2.掌握 Spring Boot 参数校验机制。 3.掌握常用校验注解(NotNull、NotBlank、Min 等)。 4.掌握 Valid 的使用方法。 5.结合全局异常处理,实现校验错误统一返回。实验环境开发工具&#xff…

右键菜单管理快速上手指南:用 ContextMenuManager 找回干净的 Windows 右键菜单

右键菜单管理快速上手指南:用 ContextMenuManager 找回干净的 Windows 右键菜单

2026/8/18 20:07:20

右键菜单管理快速上手指南:用 ContextMenuManager 找回干净的 Windows 右键菜单 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 面对一台被各种软件塞…

混合检索算法使用实例-基本混合检索

混合检索算法使用实例-基本混合检索

2026/8/18 19:57:20

初始化检索器 hybrid_retriever HybridRetriever( embedding_modelbge_model, weight_vector0.6, # 向量检索权重 weight_keyword0.4 # 关键词检索权重 ) 添加文档 documents [ {“doc_id”: “doc1”, “content”: “文档内容1”}, {“doc_id”: “doc2”, “con…

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

2026/8/17 1:28:42

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

2026/8/18 1:03:22

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

2026/8/17 8:40:51

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

多智能体大模型辩论中的立场收敛:从伪共识到理性说服的评估方法

多智能体大模型辩论中的立场收敛:从伪共识到理性说服的评估方法

2026/8/18 0:06:29

1. 从一场“假辩论”说起:为什么大模型辩论会走向“伪共识”?最近在折腾多智能体大语言模型(Multi-Agent LLM)的辩论实验,发现一个挺有意思的现象。我让几个基于GPT-4的智能体就一个争议性话题(比如“远程办…

Frida动态代码插桩框架:从原理到实战的移动安全与逆向工程指南

Frida动态代码插桩框架:从原理到实战的移动安全与逆向工程指南

2026/8/18 0:06:29

1. 从“黑盒”到“白盒”:为什么我们需要Frida在移动安全、逆向工程甚至是一些自动化测试的场景里,我们经常会遇到一个让人头疼的问题:面对一个编译好的、没有源代码的应用程序,我们如何知道它在运行时内部发生了什么?…

ECharts饼图中心文字配置指南:从label与title区别到动态交互实现

ECharts饼图中心文字配置指南:从label与title区别到动态交互实现

2026/8/18 0:06:29

1. 从“空心”到“有魂”:为什么要在饼图中间加文字?如果你用过ECharts画饼图,大概率会注意到一个现象:默认生成的饼图中间是空心的。这个设计本身没问题,它清晰地展示了各个扇区的占比关系。但在很多实际的业务场景里…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/17 12:00:53

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/15 10:10:27

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/18 12:20:24

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…