Java之动态代理 DynamicProxy
动态代理概念在学习动态代理之前先了解静态代理可参考静态代理静态代理核心缺陷类爆炸一个接口实现类必须手写一个专属代理类业务类越多代理类文件成倍增加增强逻辑无法复用日志、耗时统计等通用逻辑在每个代理类重复复制修改需要改动全部代理接口迭代维护成本极高接口新增方法所有业务类、全部静态代理类必须同步重写极易漏改运行时无法灵活适配代理类编译前手写固定无法根据运行时参数动态切换代理目标。动态代理的核心设计目标不用手动编写代理类程序运行阶段通过反射 / 字节码技术自动生成代理对象统一拦截所有目标方法一套增强逻辑适配同一接口下全部实现类彻底解决静态代理所有短板。动态代理两大分类类型依赖限制条件底层原理适用场景JDK 动态代理JDK 原生反射包目标类必须实现接口运行时生成实现目标接口的代理类有接口的 Service、Mapper、Feign 远程接口CGLIB 动态代理第三方字节码包spring 内置目标类不能是 final方法不能 final运行时生成目标类的子类重写业务方法无接口的普通业务类本文章主要以JDK 动态代理讲解动态代理核心概念JDK 动态代理依靠java.lang.reflect反射包实现运行时动态生成实现目标接口的匿名代理类所有代理对象的方法调用都会统一转发至InvocationHandler的invoke方法拦截开发者在该方法统一编写前置 / 后置增强逻辑。三大核心 API1InvocationHandler拦截处理器核心public interface InvocationHandler { /** * 代理方法统一拦截入口 * param proxy 自动生成的代理对象 * param method 当前被调用的目标方法反射对象 * param args 调用方法传入的参数数组 * return 方法执行返回值 */ Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }invoke参数讲解proxy动态生成的代理对象本身极少使用仅用于区分代理实例method本次触发调用的方法反射对象唯一标识当前执行的业务方法args调用方法传入的参数无参方法则为null。2Proxy代理生成工具类public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)ClassLoader loader类加载器使用目标对象的类加载器用于加载动态生成的代理类Class?[] interfaces目标类实现的接口数组代理类会全部实现这些接口保证多态互换InvocationHandler h自定义拦截处理器绑定到代理对象所有方法调用转发至该处理器。3Method反射方法对象封装当前调用的方法信息方法名、参数、返回值、所属类用于反射执行真实目标方法。结合代码彻底理解package demo1.basic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Scanner; public class DynamicAgent { public static void main(String[] args) { Scanner scanner new Scanner(System.in); System.out.println( 动态代理测试菜单 ); System.out.println(1Kobe 2Jordan 3pyLiu); System.out.print(请选择球员); int choice scanner.nextInt(); NBA target null; switch (choice) { case 1: target new Kobe(); break; case 2: target new Jordan(); break; case 3: target new pyLiu(); break; default: System.out.println(无效选择); return; } // 绑定拦截处理器 DogSon dogSon new DogSon(); dogSon.getTarget(target); NBA proxy (NBA) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), dogSon ); // 测试1无参无返回值方法 personalAction() System.out.println(\n---------- 调用无参方法 personalAction ----------); proxy.personalAction(); // 测试2多参数、无返回值方法 score(int, String) System.out.println(\n---------- 调用双参数无返回方法 score ----------); proxy.score(81, 季后赛单场最高分); // 测试3带参数、有返回值方法 getCareerData(int) System.out.println(\n---------- 调用带参有返回方法 getCareerData ----------); String data proxy.getCareerData(2010); System.out.println(【主线程接收方法返回值】 data); } // 自定义拦截处理器 static class DogSon implements InvocationHandler { private NBA target; void getTarget(NBA target) { this.target target; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName method.getName(); // 1、区分不同方法差异化前置增强 if (personalAction.equals(methodName)) { // 无参方法args null if (target instanceof Kobe) { System.out.print(What can I say ); } else if (target instanceof Jordan) { System.out.print(GOAT ); } else if (target instanceof pyLiu) { System.out.print(你抽中了隐藏款 ); } } else if (score.equals(methodName)) { // 带参方法打印args数组查看参数传递效果 System.out.print(【score方法前置】当前传入参数); for (Object arg : args) { System.out.print(arg ); } System.out.print( | 球员 target.getClass().getSimpleName() ); } else if (getCareerData.equals(methodName)) { System.out.print(【getCareerData方法前置】查询年份 args[0] ); } // 打印目标类名 System.out.println( target.getClass().getSimpleName()); // 2、反射执行业务方法 // 统一一行代码适配无参/多参、有返回/无返回所有方法 Object result method.invoke(target, args); // 3、后置增强处理返回值 if (result ! null) { System.out.println(【后置增强】业务方法返回内容 result); } else { System.out.println(【后置增强】该方法无返回值void); } return result; // 把业务返回值向上传递给调用方 } } // 统一业务接口3种方法覆盖 无参void、多参void、带参有返回值 interface NBA { // 无参数、无返回值 void void personalAction(); // 多个参数、无返回值 void void score(int point, String desc); // 单个参数、有返回值 String String getCareerData(int year); } // Kobe 实现类 static class Kobe implements NBA { Override public void personalAction() { System.out.println(湖人总冠军); } Override public void score(int point, String desc) { System.out.println(科比拿下 point 分记录 desc); } Override public String getCareerData(int year) { if (year 2010) { return 2010年科比拿下总冠军FMVP; } return 科比生涯数据; } } // Jordan 实现类 static class Jordan implements NBA { Override public void personalAction() { System.out.println(黑人乔丹); } Override public void score(int point, String desc) { System.out.println(乔丹拿下 point 分记录 desc); } Override public String getCareerData(int year) { return 乔丹6冠历史第一人; } } // pyLiu 实现类 static class pyLiu implements NBA { Override public void personalAction() { System.out.println(暴扣乔丹); } Override public void score(int point, String desc) { System.out.println(我大学室友 point 分记录 desc); } Override public String getCareerData(int year) { return 上理第一给; } } }建议粘贴到idea查看观察输入输出主要针对三种不同方案进行分别讲解公共前置流程程序启动控制台输入选择1创建被代理对象target new Kobe()创建拦截处理器实例DogSon dogSon new DogSon()将真实球员对象注入处理器dogSon.getTarget(target)调用Proxy.newProxyInstance()JVM 在内存自动生成动态代理类$Proxy0返回代理对象proxy走完以上步骤后分三条独立链路执行无参方法、有参方法、带返回值方法。1无参方法主线执行proxy.personalAction()自动生成的代理类$Proxy0捕获该方法调用封装三组参数proxy当前代理实例method对应personalAction()的反射方法对象argsnull无入参执行dogSon.invoke(proxy, method, null)进入拦截逻辑invoke 内部逻辑通过method.getName()判断当前是personalAction打印Kobe前置文案打印目标类名Kobe执行反射调用method.invoke(target, null)触发Kobe.personalAction()输出业务内容该方法是 void反射执行结果为null后置打印「无返回值」return null 逐层回传给主线程主线无变量接收2有参方法主线执行proxy.score(81, 季后赛单场最高分)代理类捕获调用封装三组参数proxy当前代理实例method对应score(int,String)的反射对象argsnew Object[]{81, 季后赛单场最高分}入参封装成数组执行dogSon.invoke(proxy, method, args)invoke 内部逻辑通过方法名识别为score循环遍历 args 数组打印所有传入参数打印目标类名Kobe执行method.invoke(target, args)数组内参数自动传给业务方法执行Kobe.score(81, 季后赛单场最高分)void 方法执行结果为 null后置打印「无返回值」return null 回传给主线程3有返回值方法主线执行String data proxy.getCareerData(2010)代理类捕获调用封装三组参数proxy当前代理实例method对应getCareerData(int)的反射对象argsnew Object[]{2010}执行dogSon.invoke(proxy, method, args)invoke 内部逻辑通过方法名识别为getCareerData读取args[0]获取查询年份打印前置日志打印目标类名Kobe执行method.invoke(target, args)调用Kobe.getCareerData(2010)得到返回字符串2010年科比拿下总冠军FMVP后置增强打印业务返回内容将字符串结果return向上传递主线程接收返回值存入变量data打印输出个人学习问题解惑1、代理类proxy在调用方法如.personalAction()时是如何把方法名传给拦截器DogSon中的invoke函数中的method简单来说就是我拦截器的invoke是怎么知道要执行personalAction()这个方法的通过查询在调用(NBA) Proxy.newProxyInstance时会在内存自动生成一个实现NBA接口的代理类这个类是看不见的伪代码如下// JDK自动生成的代理类 $Proxy0 implements NBA public class $Proxy0 implements NBA { // 构造时传入你写的DogSon拦截器 private final InvocationHandler h; public $Proxy0(InvocationHandler h) { this.h h; } // 重写接口无参方法 personalAction Override public void personalAction() { try { // 1. 提前预加载好当前方法的Method对象固定写死在代理类字节码里 Method m NBA.class.getDeclaredMethod(personalAction); // 2. 直接把method、代理对象、参数打包传给invoke h.invoke(this, m, null); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } // 重写接口有参方法 score Override public void score(int point, String desc) { try { Method m NBA.class.getDeclaredMethod(score, int.class, String.class); // 调用时传入的81、季后赛单场最高分 自动封装成数组args Object[] args new Object[]{point, desc}; h.invoke(this, m, args); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } // 重写带返回值方法 getCareerData Override public String getCareerData(int year) { try { Method m NBA.class.getDeclaredMethod(getCareerData, int.class); Object[] args new Object[]{year}; // 接收invoke返回的结果向上返回给主线程 Object res h.invoke(this, m, args); return (String) res; } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } }可以看到隐式的代理类$Proxy0重写了NBA接口的所有方法显式的Proxy在构造时将拦截器InvocationHandler传给了隐式的代理类$Proxy0而$Proxy0中在每个方法里都指明了Method并在调用拦截器的invoke方法时将this也就是Proxymethodargs都传给了拦截器中的invoke方法用以执行。以上内容纯属个人经验帖欢迎讨论查漏补缺。

相关新闻

为什么不接负极, 电器就 无法工作?

为什么不接负极, 电器就 无法工作?

既然正极有电(比如 5V),电已经顺着导线流进电器(芯片、灯泡)里了,电器不就该拿到能量开始工作了吗?为什么非要连回负极呢?” 水流与水车比喻(直观理解能量交换&#xff0…

2026/7/9 8:10:05 阅读更多 →
np.percentile和np.clip

np.percentile和np.clip

异常值清洗:q1, q99 np.percentile(raw, [1, 99]) raw np.clip(raw, q1, q99) # 简单的限幅假设我们现在又这么一组体温数据:raw [36.5, 36.6, 36.4, 36.7, 36.5, 36.6, 36.4, 36.5, 36.6, 36.7,36.5, 36.6, 36.4, 36.7, 36.5, 36.6, 36.4, 36.5, 3…

2026/7/9 8:10:05 阅读更多 →
南京GEO优化服务商排行榜

南京GEO优化服务商排行榜

在AI搜索流量成为企业增长新引擎的今天,南京作为长三角经济重镇,涌现出不少GEO优化服务商。但服务商水平参差不齐,稍有不慎就可能踩坑。本文结合行业实操经验,帮你梳理选择逻辑,避开常见陷阱。开篇核心结论选购GEO优化…

2026/7/9 8:10:05 阅读更多 →

最新新闻

佛山代加工贴牌推荐指南

佛山代加工贴牌推荐指南

佛山制造业高度集聚,很多品牌商和创业者都会找本地代工厂贴牌,快速切入市场。现在佛山做代加工的工厂不少,想找到产能稳定、资质齐全、能接定制单的合作方,其实是不少采购方最头疼的问题。这篇文章我结合实地探访和行业观察&#…

2026/7/9 9:10:39 阅读更多 →
音频自动分割神器:5分钟学会用Audio Slicer智能剪辑音频

音频自动分割神器:5分钟学会用Audio Slicer智能剪辑音频

音频自动分割神器:5分钟学会用Audio Slicer智能剪辑音频 【免费下载链接】audio-slicer A simple GUI application that slices audio with silence detection 项目地址: https://gitcode.com/gh_mirrors/aud/audio-slicer 还在为手动剪辑音频而烦恼吗&#…

2026/7/9 9:08:38 阅读更多 →
突破百度网盘限速!Python直链解析工具让你实现满速下载

突破百度网盘限速!Python直链解析工具让你实现满速下载

突破百度网盘限速!Python直链解析工具让你实现满速下载 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘的龟速下载而烦恼吗?每天看着几十…

2026/7/9 9:08:38 阅读更多 →
FanControl V270终极指南:告别风扇噪音,轻松掌控Windows散热方案

FanControl V270终极指南:告别风扇噪音,轻松掌控Windows散热方案

FanControl V270终极指南:告别风扇噪音,轻松掌控Windows散热方案 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.c…

2026/7/9 9:06:37 阅读更多 →
Kafka 消息可靠性投递——从生产者到消费者的全链路保障

Kafka 消息可靠性投递——从生产者到消费者的全链路保障

Kafka 消息可靠性投递——从生产者到消费者的全链路保障 一、消息可靠性的三层架构与失效模式分析 在分布式系统中,消息中间件的可靠性从来不是一个单一问题,而是一条横跨生产者、Broker、消费者三层架构的完整链路。任何一个环节的疏忽都可能导致消息丢…

2026/7/9 9:06:37 阅读更多 →
YOLOv8 推理引擎拆解:从 AutoBackend 加载到 NMS 后处理的 3 个核心模块

YOLOv8 推理引擎拆解:从 AutoBackend 加载到 NMS 后处理的 3 个核心模块

YOLOv8 推理引擎深度解构:模块化设计与工程实践指南 1. 架构设计哲学与核心模块划分 当我们需要将YOLOv8从研究原型转化为工业级应用时,模块化设计成为关键突破点。与常见端到端调用方式不同,深度解耦的架构允许开发者针对不同硬件平台进行定…

2026/7/9 9:04:36 阅读更多 →

日新闻

3大音乐平台逐字歌词完整解决方案:ESLyric-LyricsSource完全指南

3大音乐平台逐字歌词完整解决方案:ESLyric-LyricsSource完全指南

3大音乐平台逐字歌词完整解决方案:ESLyric-LyricsSource完全指南 【免费下载链接】ESLyric-LyricsSource Advanced lyrics source for ESLyric in foobar2000 项目地址: https://gitcode.com/gh_mirrors/es/ESLyric-LyricsSource 还在为Foobar2000找不到高质…

2026/7/9 0:01:04 阅读更多 →
ElegantBook封面定制揭秘:3个步骤打造专业级学术书籍

ElegantBook封面定制揭秘:3个步骤打造专业级学术书籍

ElegantBook封面定制揭秘:3个步骤打造专业级学术书籍 【免费下载链接】ElegantBook Elegant LaTeX Template for Books 项目地址: https://gitcode.com/gh_mirrors/el/ElegantBook 你是否曾经为学术书籍的封面设计而烦恼?想要一个既专业又美观的封…

2026/7/9 0:03:06 阅读更多 →
如何高效使用pyodbc:企业级数据库连接终极指南

如何高效使用pyodbc:企业级数据库连接终极指南

如何高效使用pyodbc:企业级数据库连接终极指南 【免费下载链接】pyodbc Python ODBC bridge 项目地址: https://gitcode.com/gh_mirrors/py/pyodbc 在当今数据驱动的商业环境中,企业级数据库连接已成为现代应用开发的核心需求。pyodbc作为一款强大…

2026/7/9 0:07:11 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/8 16:14:06 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/7 12:34:47 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/8 16:59:55 阅读更多 →

月新闻