尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
java之动态代理
目录[TOC](目录)前言1. 比喻理解2. 代码理解1. 签名2. 参数1ClassLoader loader (类加载器)3. 参数2Class?[] interfaces (接口数组)4. 参数3InvocationHandler h (调用处理器)3. 测试与运行4. 代理字节码原理总结前言通过一个通俗易懂的比喻快速理解 Java 动态代理的核心设计思想。通过手写 JDK 动态代理的完整代码详细剖析其核心参数与运行机制并深入 JVM 内存中的代理类字节码揭示动态代理的底层实现原理。1. 比喻理解例子大明星小美十分擅长唱歌和跳舞。打算举办演唱会举办演唱会的过程非常繁琐。小美只想把核心精力放在唱歌和跳舞上。代理小帅专门负责处理小美唱歌和跳舞之外的所有繁杂事务。赞助商希望小美来唱歌时不能直接和小美对话必须先和经纪人小帅交涉。调用过程小帅和赞助商谈妥后前置准备把小美带到舞台上小美只管唱歌核心业务。唱完歌后小帅还要负责结算收尾工作后置收尾。在这个过程中小帅就是小美的代理人也就是我们代码中常说的代理对象。2. 代码理解我们首先写一个接口叫做Star明星声明两个方法sing和dance。publicinterfaceStar{Stringsing(StringsongName);voiddance();}小美是大明星实现Star接口擅长sing与dancepublicclassBigStarimplementsStar{OverridepublicStringsing(StringsongName){System.out.println(小美正在唱songName);return谢谢大家;}Overridepublicvoiddance(){System.out.println(小美正在跳舞);}}接下来我们编写用于创建代理对象的工具类ProxyUtil。importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassProxyUtil{publicstaticStarcreateProxy(Starstar){return(Star)Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),//参数1newClass?[]{Star.class},//参数2newInvocationHandler(){//参数3OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{if(sing.equals(method.getName())||dance.equals(method.getName())){System.out.println(找演唱会地点);Objectresultmethod.invoke(star,args);System.out.println(收尾);returnresult;}returnmethod.invoke(star,args);}});}}为了让代理对象正常工作Proxy.newProxyInstance必须明确以下三个参数1. 签名publicstaticObjectnewProxyInstance(ClassLoaderloader,Class?[]interfaces,InvocationHandlerh)2. 参数1ClassLoader loader (类加载器)在程序运行过程中动态代理会在内存中实时生成代理类的字节码。为了让 JVM 识别并加载该字节码需要提供一个类加载器。通常使用当前工具类的类加载器ProxyUtil.class.getClassLoader()或被代理类的类加载器。3. 参数2Class?[] interfaces(接口数组)此参数表示代理类要实现哪些接口。在我们的例子中代理要代替明星唱歌跳舞因此需要实现Star接口写为new Class?[]{Star.class}。4. 参数3InvocationHandler h (调用处理器)当外部调用代理对象的任何方法时都会转发到此处理器的invoke方法中执行。3. 测试与运行我们编写Main类进行测试publicclassMain{publicstaticvoidmain(String[]args){// 1. 创建真实对象BigStarbigStarnewBigStar();// 2. 创建代理对象StarproxyStarProxyUtil.createProxy(bigStar);// 3. 调用代理方法StringretproxyStar.sing(Lucky);System.out.println(ret);}}运行后控制台输出如下找演唱会地点 小美正在唱Lucky 收尾 谢谢大家4. 代理字节码原理看看代理对象的伪代码importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.lang.reflect.UndeclaredThrowableException;publicfinalclass$Proxy0extendsProxyimplementsStar{privatestaticMethodm1;// 代表 Object.equals()privatestaticMethodm2;// 代表 Object.toString()privatestaticMethodm3;// 代表 Star.sing()privatestaticMethodm4;// 代表 Star.dance()static{try{m1Class.forName(java.lang.Object).getMethod(equals,Object.class);m2Class.forName(java.lang.Object).getMethod(toString);m3Class.forName(Star).getMethod(sing,String.class);m4Class.forName(Star).getMethod(dance);}catch(Exceptione){thrownewNoSuchMethodError(e.getMessage());}}public$Proxy0(InvocationHandlerh){super(h);// 将处理器传递给父类 java.lang.reflect.Proxy}OverridepublicfinalStringsing(StringsongName){try{// 所有方法调用都会转发到超类持有的 InvocationHandler 的 invoke 方法中return(String)super.h.invoke(this,m3,newObject[]{songName});}catch(Error|RuntimeExceptione){throwe;}catch(Throwablet){thrownewUndeclaredThrowableException(t);}}Overridepublicfinalvoiddance(){try{super.h.invoke(this,m4,null);}catch(Error|RuntimeExceptione){throwe;}catch(Throwablet){thrownewUndeclaredThrowableException(t);}}}动态生成的代理类命名为$Proxy0它继承了java.lang.reflect.Proxy并且实现了目标接口Star。Java 不支持多重继承$Proxy0已经占用了继承Proxy的名额。所以只能代理接口无法代理实例化的类.$Proxy0的构造方法接收一个InvocationHandler h并通过super(h)传给父类Proxy统一保管。在重写的接口方法如sing、dance中没有包含任何业务逻辑而是直接调用super.h.invoke(this, method, args).总结动态代理的核心思想是解耦通过代理对象将繁琐的非核心业务与真实对象的核心业务隔离开来。JDK 动态代理利用java.lang.reflect.Proxy类的newProxyInstance方法在运行期动态地在内存中生成实现了指定接口的代理类字节码。代理类如$Proxy0继承自Proxy并实现目标接口其内部持有一个InvocationHandler对象所有接口方法的调用都会被重定向并转发到该处理器的invoke方法中执行。
RELATED

相关推荐

CMake GUI 3.28.3 与 VS 2019/2022 协同配置:5步解决“project files may be invalid”

CMake GUI 3.28.3 与 VS 2019/2022 协同配置:5步解决“project files may be invalid”

CMake GUI 3.28.3 与 Visual Studio 协同配置全指南:从报错到生成的完整解决方案最近在Windows平台上使用CMake构建项目时,不少开发者遇到了"Error in configuration process, project files may be invalid"这个令人头疼的错误提示。作为一名…

📅 2026/7/24 11:41:15
C++11 随机数生成——告别 rand()

C++11 随机数生成——告别 rand()

文章目录C11 随机数生成——告别 rand()一、C 语言的 rand() 有什么问题二、C11 的三大组件三、实战例子3.1 模拟温度:42C 上下波动 2C3.2 模拟误码率:极小数值3.3 生成整数范围四、逐行拆解std::random_device{}()std::mt19937 rng{种子}std::uniform_r…

📅 2026/7/24 17:33:46
Node.js 18+ 开发环境 HTTP 431 报错:3种主流构建工具(Vite/Webpack/Vue CLI)的 `--max-http-header-size` 配置实战

Node.js 18+ 开发环境 HTTP 431 报错:3种主流构建工具(Vite/Webpack/Vue CLI)的 `--max-http-header-size` 配置实战

Node.js 18 开发环境 HTTP 431 报错:3种主流构建工具的--max-http-header-size配置实战当你在使用 Node.js 18 进行前端开发时,突然遇到 HTTP 431 错误(Request Header Fields Too Large),这通常意味着你的请求头过大&…

📅 2026/9/13 17:17:05
MORE NEWS

更多资讯

📰

SQL五大分类详解:从DDL到TCL,索引优化与慢SQL排查实战

1. SQL分类全景图:别再傻傻分不清很多朋友学MySQL,上来就背了一堆命令,结果真到了写业务SQL、排查慢查询、面试被问底层原理的时候,脑子里还是一团浆糊。为什么?因为大家习惯按“命令”去记,而不是按“职责…

📰

MCP 保姆级教程,这次用 TaoToken 走通 Cursor 模型调用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

MCP开发实战:从Tool设计到生产部署避坑指南

1. 从"智能体调工具"说起:MCP在AI应用里的生态位我第一次认真琢磨MCP(Model Context Protocol),是因为一个挺尴尬的场景:模型已经在对话里答得头头是道,可一旦需要它去查一下数据库、走一个内部接…

📰

3 步搞定 Klipper 温度优化:从 PID 校准到波动排查的完整指南

3 步搞定 Klipper 温度优化:从 PID 校准到波动排查的完整指南 【免费下载链接】klipper Klipper is a 3d-printer firmware 项目地址: https://gitcode.com/GitHub_Trending/kl/klipper 周五晚上,ABS 盒子打印到第 70 分钟,热床温度突…

📰

MCP协议实现Blender智能操控:从CLI到Prompt Skill的工程实践

1. 先说清楚:GPT-6 Astra 并不存在,但这个标题背后藏着真实的技术演进路径 “用GPT-6 Astra操控Blender,保姆级教程来了。”——看到这个标题,我第一反应是点开前先截图存证。不是因为激动,而是因为职业本能&#xff1…

📰

MyEMS+CNN-LSTM:设备预测性维护从零到92%准确率的实战

接到值班同事电话的时候是早上六点半,三号空压机报警停机。我到现场打开柜门,轴承已经烧得发蓝,润滑油碳化成一圈黑渣。事后算账:非计划停机八小时,后段注塑产线全部等料,损失够买两台新电机。那会儿我就在…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬