大家好,我是小米,今年31岁,程序员一枚,目前在南京做后端开发,热爱开源、爱折腾、爱喝奶茶、爱在地铁上复盘面试题。今天这篇文章,要给大家分享一个我上周刚面过的一道 Java 社招经典题目:

“JDK 动态代理和 CGLIB 动态代理的区别?”

讲真,当时我脑海里只飘过四个字:

“一个接口,一个类?”

面试官微微一笑,“能展开说说吗?”

我愣了愣,这时候我才意识到,是时候系统地复习一下动态代理这个老朋友了。

所以,今天这篇文章,不只是帮你复习面试题,还会从我自己的项目经验、源码分析、踩坑记录出发,用讲故事的方式带你真正搞懂这两个经典代理方式的区别,甚至延伸讲到Spring AOP里的应用!

那天面试官突然问我“代理模式了解吗?”

上周五下午,我去面了南京一家金融科技公司(大厂不敢说,小而美是真的),一面聊项目、二面聊架构,三面就开始考察底层原理了。

其中一道题就是:

“你在项目中有没有用过 JDK 动态代理?它和 CGLIB 有什么区别?”

我当时确实用过,但从来没有系统梳理过这俩到底差在哪。于是,趁着周末我回家把这事儿搞了个彻底。

我和动态代理的“初识”

还记得我第一次接触“代理模式”是在看《Head First 设计模式》的时候,那时候只知道代理就是“帮别人干活”,比如打印日志、权限校验、事务控制这些“增强操作”。

但在 Java 世界里,“代理”真正牛的地方是——

在不修改源码的情况下,实现功能增强!

这点,JDK 和 CGLIB 动态代理都能做到,但实现原理截然不同。

JDK 动态代理:接口才是老大哥

我先来说说 JDK 动态代理。

1、基本原理

JDK 动态代理需要“接口”。代理类并不会继承目标类,而是实现目标类实现的接口

举个栗子:

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_动态代理

代理类:

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_代理类_02

2、特点小结

  • 依赖接口,目标类必须实现接口
  • 底层通过 反射+字节码拼接生成代理类
  • 性能不如 CGLIB(因为反射调用有损耗)
  • 优点:标准、安全、稳定

如果你写的是接口+实现类风格的业务代码,用 JDK 动态代理就很合适。

CGLIB 动态代理:接口?我不需要!

再来看看 CGLIB 动态代理。

1、基本原理

CGLIB 采用的是 继承的方式,代理类是目标类的子类,通过继承+重写来实现“增强”。

还是刚才的 UserServiceImpl,我们可以不写接口,直接代理这个类。

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_AOP_03

2、特点小结

  • 不依赖接口,可以代理普通类
  • 通过继承生成子类的方式创建代理类
  • 性能优于 JDK 动态代理(不走反射,调用快)
  • 目标类不能是 final,方法不能是 final/static
  • 存在类加载问题(和 Spring AOP 结合时可能踩坑)

如果你写的是“面向实现类”的代码,或者第三方库没有接口,CGLIB 就是你的好朋友!

来一波硬核对比表(建议收藏)

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_代理类_04

项目实战中我是怎么选的?

我最近在做一个接口限流组件,需要对 Controller 层打日志、记录请求时间,还要做幂等校验。我们的 Controller 层用的是 SpringMVC,都是接口实现类,所以我选择了 JDK 动态代理+注解的方式。

但我也遇到一个坑!

我一开始想代理某个三方类,发现它没有接口,结果 JDK 代理直接报错:

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_动态代理_05

于是我换了 CGLIB,完美解决。但后来又遇到:

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_AOP_06

哦豁,这时候我只想说:final 是万恶之源!

Spring AOP 背后的秘密

很多小伙伴都会问:

“Spring AOP 是用哪个代理方式的?”

答案是:

两者都用,看情况!

Spring 的策略是这样的:

  • 如果目标对象实现了接口,默认使用 JDK 动态代理
  • 如果目标对象没有接口,使用 CGLIB 动态代理

你也可以强制设置:

Java社招面试题:JDK动态代理和CGLIB动态代理的区别?我在面试时被问懵了!_AOP_07

为什么 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岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!