大家好,我是小米,今年31岁,程序员一枚,目前在南京做后端开发,热爱开源、爱折腾、爱喝奶茶、爱在地铁上复盘面试题。今天这篇文章,要给大家分享一个我上周刚面过的一道 Java 社招经典题目:
“JDK 动态代理和 CGLIB 动态代理的区别?”
讲真,当时我脑海里只飘过四个字:
“一个接口,一个类?”
面试官微微一笑,“能展开说说吗?”
我愣了愣,这时候我才意识到,是时候系统地复习一下动态代理这个老朋友了。
所以,今天这篇文章,不只是帮你复习面试题,还会从我自己的项目经验、源码分析、踩坑记录出发,用讲故事的方式带你真正搞懂这两个经典代理方式的区别,甚至延伸讲到Spring AOP里的应用!
那天面试官突然问我“代理模式了解吗?”
上周五下午,我去面了南京一家金融科技公司(大厂不敢说,小而美是真的),一面聊项目、二面聊架构,三面就开始考察底层原理了。
其中一道题就是:
“你在项目中有没有用过 JDK 动态代理?它和 CGLIB 有什么区别?”
我当时确实用过,但从来没有系统梳理过这俩到底差在哪。于是,趁着周末我回家把这事儿搞了个彻底。
我和动态代理的“初识”
还记得我第一次接触“代理模式”是在看《Head First 设计模式》的时候,那时候只知道代理就是“帮别人干活”,比如打印日志、权限校验、事务控制这些“增强操作”。
但在 Java 世界里,“代理”真正牛的地方是——
在不修改源码的情况下,实现功能增强!
这点,JDK 和 CGLIB 动态代理都能做到,但实现原理截然不同。
JDK 动态代理:接口才是老大哥
我先来说说 JDK 动态代理。
1、基本原理
JDK 动态代理需要“接口”。代理类并不会继承目标类,而是实现目标类实现的接口。
举个栗子:
代理类:
2、特点小结
- 依赖接口,目标类必须实现接口
- 底层通过 反射+字节码拼接生成代理类
- 性能不如 CGLIB(因为反射调用有损耗)
- 优点:标准、安全、稳定
如果你写的是接口+实现类风格的业务代码,用 JDK 动态代理就很合适。
CGLIB 动态代理:接口?我不需要!
再来看看 CGLIB 动态代理。
1、基本原理
CGLIB 采用的是 继承的方式,代理类是目标类的子类,通过继承+重写来实现“增强”。
还是刚才的 UserServiceImpl,我们可以不写接口,直接代理这个类。
2、特点小结
- 不依赖接口,可以代理普通类
- 通过继承生成子类的方式创建代理类
- 性能优于 JDK 动态代理(不走反射,调用快)
- 目标类不能是 final,方法不能是 final/static
- 存在类加载问题(和 Spring AOP 结合时可能踩坑)
如果你写的是“面向实现类”的代码,或者第三方库没有接口,CGLIB 就是你的好朋友!
来一波硬核对比表(建议收藏)
项目实战中我是怎么选的?
我最近在做一个接口限流组件,需要对 Controller 层打日志、记录请求时间,还要做幂等校验。我们的 Controller 层用的是 SpringMVC,都是接口实现类,所以我选择了 JDK 动态代理+注解的方式。
但我也遇到一个坑!
我一开始想代理某个三方类,发现它没有接口,结果 JDK 代理直接报错:
于是我换了 CGLIB,完美解决。但后来又遇到:
哦豁,这时候我只想说:final 是万恶之源!
Spring AOP 背后的秘密
很多小伙伴都会问:
“Spring AOP 是用哪个代理方式的?”
答案是:
两者都用,看情况!
Spring 的策略是这样的:
- 如果目标对象实现了接口,默认使用 JDK 动态代理
- 如果目标对象没有接口,使用 CGLIB 动态代理
你也可以强制设置:
为什么 Spring 这么设计?原因有两个:
- 避免使用不必要的 CGLIB,防止类加载问题
- JDK 代理相对更稳定,优先使用接口方式更安全
面试的时候怎么答这道题?
假设你也在社招面试中被问到“JDK 动态代理和 CGLIB 的区别”,可以这么答:
答:
JDK 动态代理是 Java 标准库提供的代理方式,要求目标类必须实现接口。它通过反射生成代理类,实现接口并转发方法调用。缺点是性能略低,且不能代理没有接口的类。
而 CGLIB 动态代理是通过继承目标类的方式生成子类,重写方法实现增强。它不依赖接口,性能更优,但不能代理 final 类和 final 方法。
在 Spring AOP 中,两者都被使用,默认是 JDK 动态代理,如果目标类没有接口或设置了 proxy-target-class=true,则使用 CGLIB。
如果你能再举个项目中用到代理的例子,面试官一定觉得你是真的理解了这个知识点!
写在最后:三句话总结
最后,我用三句话总结本文精华:
- JDK 动态代理需要接口,反射实现,适用于接口驱动的设计
- CGLIB 动态代理基于继承,性能更优,但不支持 final
- Spring AOP 两者兼容,根据是否实现接口自动选择
END
如果你觉得这篇文章对你有帮助,欢迎转发、在看、收藏三连!
你也可以在留言区和我聊聊你在用代理时踩过的那些坑~我是小米,一个爱分享的技术人,我们下篇文章见!
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!