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] 动态代理的实现原理 - 简书

相关新闻

文献综述不是“八卦论坛”:书匠策AI让我学会了一种叫“拼图式”的文献梳理法

文献综述不是“八卦论坛”:书匠策AI让我学会了一种叫“拼图式”的文献梳理法

官网:www.shujiangce.com | 微信公众号:书匠策AI 先抛一个我自己反思了很久的观点。 过去八年,我在课堂上教文献综述,讲的无非是“检索-阅读-归纳-批评”四步法。直到有一天,一个学生拿着我给的范文问我&#xff1…

2026/8/18 19:17:29 阅读更多 →
ArcGIS实验教程——实验三十三:ArcScan自动矢量化完整案例教程

ArcGIS实验教程——实验三十三:ArcScan自动矢量化完整案例教程

ArcGIS实验视频教程合集:《ArcGIS实验教程从入门到精通》(附配套实验数据)》 文章目录 1 ArcScan 简介 2. ArcScan使用前提及注意事项 3. ArcGIS自动矢量化案例 1 ArcScan 简介 ArcScan是ArcGIS Desktop的打展模块,是栅格数据矢量化的套工具集, 用这些工具,可以创建要素…

2026/8/18 19:16:29 阅读更多 →
零基础学 AI 漫剧(建议收藏)

零基础学 AI 漫剧(建议收藏)

本次整理分享的是一些零基础学习AI的资料,网盘资料建议大家收藏,分享永久有效,如果后续做调整删除文件的话可能会提示分享不存在,此次的分享针对没有基础从头学的小伙伴们比较友好些,有需要的赶紧收藏起来吧 「AI漫剧…

2026/8/18 19:16:29 阅读更多 →

最新新闻

AI智能体CLI/TUI架构设计:从交互范式到分层实现

AI智能体CLI/TUI架构设计:从交互范式到分层实现

1. 从命令行到智能体:为什么我们需要一个“会思考”的终端界面?如果你和我一样,每天有超过一半的时间泡在终端里,那你肯定经历过这样的场景:敲下一长串命令,等待结果,然后根据结果再敲下一串命令…

2026/8/19 3:07:09 阅读更多 →
智能温控器DIY:从硬件设计到软件实现的全流程解析

智能温控器DIY:从硬件设计到软件实现的全流程解析

1. 项目概述:从“调温器”到“家庭能源中枢”几年前,我还在为一个问题头疼:冬天出门忘了关暖气,回家时屋里冷得像冰窖,还得等上半小时才能暖和起来;夏天又总担心空调开太久电费爆表。直到我开始折腾“Smart…

2026/8/19 3:07:09 阅读更多 →
基于事件驱动架构的AI Agent终端界面设计与实现

基于事件驱动架构的AI Agent终端界面设计与实现

1. 项目概述:为什么我们需要一个终端里的 Agent 界面? 如果你和我一样,每天大部分时间都泡在终端里,那你肯定对传统的命令行交互(CLI)又爱又恨。爱的是它的高效和脚本化能力,恨的是它那冷冰冰的…

2026/8/19 3:07:09 阅读更多 →
树莓派5与ROS 2:构建高性能复合机器人的软硬件全攻略

树莓派5与ROS 2:构建高性能复合机器人的软硬件全攻略

1. 项目概述:为什么是“Pi 5 ROS 2”的终极组合?如果你玩过树莓派,也听说过机器人操作系统ROS,那么“Pi 5 Powerhouse: Building the Ultimate ROS 2 Composite Bot”这个项目标题,可能会让你心头一热。它描绘的&…

2026/8/19 3:07:09 阅读更多 →
大语言模型常识推理中的显著性偏差:机制、评测与缓解策略

大语言模型常识推理中的显著性偏差:机制、评测与缓解策略

最近在调研大语言模型(LLM)的常识推理能力时,发现一个有趣但容易被忽视的现象:模型在面对看似简单的选择题时,其判断逻辑可能并非基于常识本身,而是被问题中某些“显眼”的词汇或表述所主导。这就像问你“你…

2026/8/19 3:07:09 阅读更多 →
Apache Fluss 湖流一体架构解析:实时数仓与 AI Agent 数据底座

Apache Fluss 湖流一体架构解析:实时数仓与 AI Agent 数据底座

1. 项目概述:从 Flink 的“表弟”到 Apache 的“新星”如果你最近在关注大数据和实时计算领域,那么“Apache Fluss”这个名字一定在你的时间线上高频出现。就在不久前,Apache 软件基金会正式宣布 Fluss 从孵化器毕业,成为顶级项目…

2026/8/19 3:06:09 阅读更多 →

日新闻

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/19 0:00:30 阅读更多 →
AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

1. 项目概述:当AI开始“猜”数学定理 最近在AI研究圈里,一个名为“Moonshine”的项目引起了不小的讨论。这名字本身就挺有意思,直译是“月光”,但在数学史上,它特指一个神秘而美丽的联系——魔群月光猜想,连…

2026/8/19 0:00:30 阅读更多 →
WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 一台刚配的新电脑,跑《魔兽争霸3》却卡成 PPT——这…

2026/8/19 0:02:31 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

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

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

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/17 18:54:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/17 18:55:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →