JDK动态代理和CGLIB代理的机制和选择

news/2026/1/25 20:04:02/文章来源:https://www.cnblogs.com/guoyafenghome/p/19530391

JDK动态代理和CGLIB代理的机制和选择

一、实现原理的本质区别

JDK动态代理是基于接口实现的。它利用Java反射机制,在运行时动态生成一个实现了目标接口的代理类。这个代理类实现了跟目标对象相同的接口,当调用接口方法时,会转发给InvocationHandler的invoke方法,我们在invoke方法里可以添加增强逻辑,然后再通过反射调用真实对象的方法。CGLIB代理是基于继承实现的。CGLIB的全称是Code Generation Library,它是一个强大的字节码生成库。CGLIB在运行时动态生成目标类的子类,通过覆盖父类的方法来实现增强。当调用方法时,会先执行子类(代理类)的逻辑,然后通过super调用父类(真实对象)的方法。这是最核心的区别:一个依赖接口,一个依赖继承。

二、使用前提和限制

JDK动态代理的要求:
  • 必须有接口:目标类必须实现至少一个接口,否则无法使用
  • 代理对象和目标对象都实现相同的接口
  • 只能代理接口中定义的方法
CGLIB代理的要求:
  • 不需要接口:普通类就可以代理
  • 但有一些限制:
  • 目标类不能是final类,因为需要生成子类
  • 目标方法不能是final方法,因为子类无法覆盖final方法
  • 目标方法不能是static方法,static方法属于类不属于实例,无法被子类覆盖
  • 目标方法不能是private方法,private方法子类无法访问

三、性能对比

这个问题很多人有误解,我说说实际情况:早期版本(JDK 1.6及以前):
  • JDK动态代理比较慢,因为每次方法调用都要通过反射
  • CGLIB快很多,因为它直接生成字节码,调用是通过FastClass机制,避免了反射
现代版本(JDK 1.8及以后):
  • JDK动态代理经过大量优化,性能已经非常接近CGLIB了
  • 在某些场景下,JDK动态代理甚至比CGLIB更快
  • 实际项目中,两者的性能差异基本可以忽略不计
我在实际项目中做过测试,对于普通业务场景,两者的性能差异在纳秒级别,完全不是瓶颈所在。创建代理对象的速度:
  • JDK动态代理创建代理对象更快,因为逻辑相对简单
  • CGLIB创建代理对象较慢,因为需要生成子类的字节码并加载
方法调用速度:
  • 现代JDK版本下,两者差异很小
  • CGLIB有FastClass机制,避免反射调用
  • 但JDK的反射也有方法内联等优化

四、Spring中的选择策略

Spring框架对代理方式的选择有一套自动决策机制:默认策略(Spring 4.x及之前):
  • 如果目标对象实现了接口 → 使用JDK动态代理
  • 如果目标对象没有实现接口 → 使用CGLIB代理
Spring Boot 2.x之后的变化:
  • Spring Boot 2.x默认强制使用CGLIB代理,即使有接口也用CGLIB
  • 原因是避免接口和实现类注入时的一些问题
强制指定代理方式:可以通过配置强制使用CGLIB:
  • 配置文件设置:spring.aop.proxy-target-class=true
  • 或者注解:@EnableAspectJAutoProxy(proxyTargetClass = true)
设置为true就强制使用CGLIB,false就是默认策略。

五、两者的优缺点对比

JDK动态代理:优点:
  • JDK原生支持,不需要额外依赖
  • 代理对象生成速度快
  • 更符合"面向接口编程"的设计原则
缺点:
  • 必须有接口,限制较大
  • 只能代理接口方法,如果直接调用实现类的特有方法就代理不到
CGLIB代理:优点:
  • 不需要接口,适用范围更广
  • 可以代理普通类
  • Spring的@Configuration类的增强就是用CGLIB
缺点:
  • 需要引入额外的依赖(不过Spring已经内置了)
  • 不能代理final类和final方法
  • 创建代理对象稍慢一些
  • 子类会继承父类的所有方法,类体积可能较大

六、实际项目中的选择建议

根据我的实践经验:推荐用CGLIB的场景:
  • 开发Spring Boot应用,直接用默认配置(Spring Boot 2.x已经默认CGLIB)
  • 目标类没有实现接口
  • 不想因为代理问题强制定义接口
  • 需要代理@Configuration配置类(必须用CGLIB)
推荐用JDK动态代理的场景:
  • 已经有良好的接口设计,严格遵循面向接口编程
  • 目标类是final的(虽然这种情况不多)
  • 想减少对第三方库的依赖
  • 明确只需要代理接口方法
我的实践建议:说实话,在现代Spring Boot项目中,我一般不会特意去选择,就用框架的默认配置(CGLIB)。原因是:
  1. 性能差异可以忽略
  1. CGLIB更灵活,不会因为没接口而出问题
  1. Spring Boot团队选择CGLIB作为默认,说明这是经过深思熟虑的
除非有特殊需求,比如严格的架构规范要求面向接口,或者遗留项目已经是JDK代理,才会去调整。

七、常见的坑和问题

我在项目中遇到过几个相关的问题:问题1:注入类型不匹配如果用JDK动态代理,代理对象的类型是接口,不能强转成实现类。比如:
  • 定义了UserService接口和UserServiceImpl实现
  • JDK代理生成的代理对象类型是UserService
  • 如果你用@Autowired注入UserServiceImpl类型,就会报错
这时要么改成注入接口类型,要么改用CGLIB代理。问题2:final方法无法被代理用CGLIB时,如果方法是final的,切面不会生效。我之前就遇到过一个坑:一个Service方法加了@Transactional,但方法被标记成final了,结果事务死活不生效,排查了半天才发现。问题3:private方法调用不管是JDK还是CGLIB代理,都无法代理private方法。有些同学在类内部调了private方法,发现事务或缓存注解不生效,就是这个原因。问题4:同类内部调用这是最常见的坑。类内部的方法互相调用,比如methodA调用methodB,这时调用的是this.methodB(),不是代理对象,所以切面不生效。要解决要么把方法拆到另一个Bean,要么自己注入自己(虽然不优雅),要么用AspectJ。

八、底层实现的一点补充

JDK动态代理:
  • 使用Proxy.newProxyInstance创建代理对象
  • 需要传入类加载器、接口数组、InvocationHandler
  • 生成的代理类名字类似$Proxy0、$Proxy1这样
  • 所有方法调用都会转到InvocationHandler.invoke
CGLIB代理:
  • 使用Enhancer类生成子类
  • 通过ASM直接操作字节码
  • 使用MethodInterceptor拦截方法调用
  • 生成的类名类似UserService$$EnhancerBySpringCGLIB$$12345678
  • 使用FastClass机制提高调用效率,避免反射

总结

简单来说:
  • JDK动态代理:基于接口、使用反射、JDK原生、有接口才能用
  • CGLIB代理:基于继承、生成字节码、功能更强、不能代理final
选择建议:
  • Spring Boot 2.x项目:用默认的CGLIB就好
  • 有良好接口设计的项目:JDK动态代理也完全够用
  • 性能不是选择的主要因素,两者差异可以忽略
  • 关键是理解限制:final类/方法、内部调用等问题
在实际工作中,我们更应该关注的是如何正确使用AOP,避免常见的坑,而不是纠结用哪种代理方式。毕竟Spring已经帮我们做了最优选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1215603.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

洛谷 P1332 血色先锋队 题解

题目链接 洛谷 P1332 血色先锋队 思路分析 一道广搜的题目。按照题意,首先将每个感染源作为起点,对全军跑一次广搜,对每个成员记录其感染的时间。然后只需依次输出每个领主的感染时间即可。 代码呈现 #include<b…

ClickHouse 25.12 版本发布说明

本文字数&#xff1a;20006&#xff1b;估计阅读时间&#xff1a;51分钟 作者&#xff1a;ClickHouse Team 本文在公众号【ClickHouseInc】首发 又一个月过去了&#xff0c;这也意味着新版本如期而至&#xff01; 发布概要 ClickHouse 25.12 版本带来了 26 项新特性 &#x1f3…

什么才是真正影响性能的关键:一年来基准测试的经验教训

本文字数&#xff1a;4369&#xff1b;估计阅读时间&#xff1a;11 分钟作者&#xff1a;Tom Schreiber为什么基准测试总是贯穿于我的工作年末将至&#xff0c;我习惯回顾一些最令我自豪的工作成果。2025年我创作并协助发布了大量与 ClickHouse 相关的内容&#xff0c;但其中最…

大数据领域HBase的RegionServer管理技巧

大数据领域HBase的RegionServer管理技巧&#xff1a;从新手到高手的进阶指南 关键词&#xff1a;HBase、RegionServer、Region管理、MemStore刷写、WAL日志、负载均衡、集群调优 摘要&#xff1a;在大数据存储领域&#xff0c;HBase作为Apache顶级项目&#xff0c;凭借其高并发…

ClickHouse 完成由 Dragoneer 领投的 4 亿美元 D 轮融资,加速其在分析与 AI 基础设施领域的扩张

本文字数&#xff1a;6065&#xff1b;估计阅读时间&#xff1a;13 分钟作者&#xff1a;ClickHouse Team本文在公众号【ClickHouseInc】首发公司收购 Langfuse&#xff0c;正式进军 LLM 可观测性 (LLM observability) 领域&#xff0c;并推出原生 Postgres 服务&#xff0c;以…

走向全栈:前后端状态认知差异与设计边界的深度探讨

文章目录 引言&#xff1a;为何关注前后端状态认知差异全栈开发的兴起与前后端分离的现状状态管理在现代应用中的重要性前后端协作中的常见误解 登录态的归属&#xff1a;前端状态还是后端状态&#xff1f;登录态的定义与实现方式前端如何管理登录态后端对登录态的支持与要求案…

Java毕设选题推荐:基于Java的小说三体科幻社区管理系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

本周 GitHub 热门:更好用的MCP客户端和Coding创作视频,开源项目层出不穷!

文章目录 大盘快读AI 助手的崛起iOfficeAI/AionUimicrosoft/agent-lightningVectifyAI/PageIndexeigent-ai/eigent 视频创作的新视角多样化的开发工具与框架obra/superpowerstobi/try 总结与展望参考来源 大盘快读 随着人工智能和视频技术的快速发展&#xff0c;开源社区再次迎…

计算机Java毕设实战-基于springboot的三体科幻社区交流分享管理系统的设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

Java毕设项目:基于springboot的三体科幻社区管理系统的设计与实现(源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

【课程设计/毕业设计】基于vue+springboot科幻社区管理系统springboot的三体科幻社区管理系统的设计与实现【附源码、数据库、万字文档】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

生成器跟容器还是不一样的,生成器可能有“江郎才尽”的那一天 - GLORY-TO-THE

1.虽然我们平时从运行效果上来说,经常管“生成器”就理解成“容器”。 但是千万别忘了,生成器和列表等容器是不一样的,生成器是“惰性机制”。 要点1:我们在和生成器要值的时候,它是现做现卖的。当我们跟生成器取…

ppo走中国象棋如果走到某一步3步之内必死会怎么样

在使用 PPO&#xff08;Proximal Policy Optimization&#xff0c;近端策略优化&#xff09; 这类强化学习算法训练中国象棋 AI 时&#xff0c;如果 AI 走到某一步后&#xff0c;在接下来的 3 步之内必死&#xff08;即被将死&#xff09;&#xff0c;那么具体会发生以下几种情…

03.Python IDE / 编辑器选型指南:PyCharm/VS Code/IDLE 使用对比

目录前言一、主流 Python IDE / 编辑器介绍&#xff1a;不同 “工作台” 的特点1.1 IDLE&#xff1a;Python 自带的 “简易小书桌”生活化类比核心特点界面直观1.2 VS Code&#xff1a;轻量可定制的 “多功能折叠桌”生活化类比核心特点界面直观1.3 PyCharm&#xff1a;专业的 …

【已解决】浏览器出现 STATUS_STACK_BUFFER_OVERRUN 错误的原因及解决方法汇总

浏览器出现 STATUS_STACK_BUFFER_OVERRUN 错误的原因及解决方法汇总 文章目录浏览器出现 STATUS_STACK_BUFFER_OVERRUN 错误的原因及解决方法汇总一、问题描述二、STATUS_STACK_BUFFER_OVERRUN 是什么错误&#xff1f;三、常见解决方法&#xff08;按推荐顺序&#xff09;方法1…

5060laptop 显卡安装torch

5060laptop 显卡安装torchpip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu130 跑通效果展示

详细介绍:LLaMA大模型家族发展介绍

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

模拟 TI C6678 多核并行加速的雷达目标检测与协同处理

1. 信号处理架构与参数定义 在 MATLAB 仿真开始前,需模拟硬件节点的分配: 数据结构:定义两个波束的回波数据矩阵。 计算节点模拟: FPGA 模块:模拟高速并行处理(运补、脉压、积累)。 DSP A/B 模块:模拟 8 核并行搜索逻辑。 中心控制逻辑:模拟多 DSP 结果融合与 3/5…

selenium自动化测试工具实战项目(登录页面)

介绍测试的系统&#xff1a;白月黑羽网站的测试系统(白月SMS系统)测试的功能&#xff1a;登录&#xff0c;退出登录。测试用例用例编号测试模块前置条件测试步骤预期结果实际结果Login_01登录功能已注册1.输入正确的用户名&#xff0c;输入错误的密码。2.点击登录登录不成功登录…

字体反爬分析

1.字体反爬分析 【字体加密】 自定义文本的编码方式;将我们能看懂的字符按自定义的编码方式进行传输;使用浏览器进行渲染或解析该字体时需要按指定的编码方式才能正确解析。编码方式一般为字体文件。 【项目分析】 通过selenium动态加载网页获取车辆信息数字格式为:\uhhh 通…