本文用「周杰伦开演唱会」的真实场景,把代理模式的 3 种实现讲透,代码可直接运行,文末附对比表,新手也能秒懂!

一、代理模式是个啥?—— 经纪人的 “生存法则”

先问大家一个问题:周杰伦开演唱会时,会自己去和主办方面谈、签合同、订机票、收定金吗?

答案肯定是:不会!周董只需要专心唱歌,剩下的琐事全交给经纪人搞定 —— 而这个 “经纪人替明星处理琐事、控制明星业务访问” 的逻辑,就是编程里的「代理模式」!

1.1 代理模式的核心作用

通过 “代理对象” 控制对 “真实对象” 的访问,在调用真实对象的方法前做前置处理(比如经纪人谈合同)、调用后做后置处理(比如经纪人收尾款),核心是 “增强功能,控制访问”。

1.2 代理模式的三大角色(周杰伦版)

表格

角色类型

对应场景

核心职责

抽象角色(接口)

明星业务规范

定义明星要做的事(唱歌、面谈等)

真实角色

周杰伦本人

实现核心业务(只负责唱歌)

代理角色

周杰伦的经纪人

代理真实角色,处理琐事 + 调用核心业务

二、静态代理:专属经纪人,只为周杰伦服务

静态代理就像 “专属经纪人”—— 只服务周杰伦一个人,经纪人和周杰伦都得遵守 “明星业务规范”(实现同一个接口)。

2.1 第一步:定义 “明星业务接口”(抽象角色)

先定规矩:明星要做哪些事?

2.2 第二步:周杰伦本人(真实角色)

周董只干核心活 —— 唱歌,其他事一概不管:

2.3 第三步:专属经纪人(代理角色)

经纪人替周董处理所有琐事,只在 “唱歌” 时喊周董出场:

2.4 第四步:测试 —— 一场演唱会的完整流程

2.5 运行结果

2.6 静态代理的 “槽点”(为啥不够香?)

  1. 代码重复严重:经纪人(代理类)和周杰伦(实现类)都要实现Star接口,接口方法越多,重复代码越多;
  2. 通用性差:这个经纪人只服务周杰伦,如果要给 “刘德华”“张学友” 找经纪人,得重新写一套代理类 —— 太麻烦!

三、JDK 动态代理:万能经纪人,能服务所有明星

JDK 动态代理就像 “万能经纪人”—— 不用为每个明星单独写代理类,一套代码能服务所有实现了接口的明星,彻底解决静态代理的痛点!

3.1 第一步:简化接口(抽象角色)

只保留核心业务(唱歌),突出重点:

3.2 第二步:周杰伦依旧只唱歌(真实角色)

3.3 第三步:打造 “万能经纪人工厂”(代理工厂)

通过反射动态生成代理对象,支持所有实现接口的明星:

3.4 第四步:测试 —— 万能经纪人服务周杰伦

3.5 运行结果

3.6 JDK 动态代理的 “小优势”

  1. 无代码重复:不用和真实角色实现同一个接口,代理逻辑集中在InvocationHandler中;
  2. 通用性强:一套工厂代码能代理所有实现了接口的对象(换刘德华只需要new ProxyFactory(new RealLiuDeHua()));
  3. 动态生成:代理类在运行时动态生成,不用手动编写代理类。

四、CGLIB 动态代理:无接口也能代理的 “超级经纪人”

JDK 动态代理有个小缺点 —— 必须要求真实角色实现接口!如果周杰伦 “不想签接口”(没实现任何接口),咋办?

CGLIB 登场!它不用接口,直接生成真实角色的子类作为代理对象,堪称 “超级经纪人”~

4.1 第一步:引入 CGLIB 依赖

如果项目里有 Spring-Core,可直接用(Spring 内置了 CGLIB);否则手动引入:

4.2 第二步:周杰伦不用接接口(真实角色)

4.3 第三步:超级经纪人工厂(CGLIB 代理工厂)

4.4 第四步:测试 —— 无接口也能搞定

4.5 运行结果

4.6 CGLIB 的 “小特点”

  1. 无需接口:突破 JDK 动态代理的限制,代理任意类(只要不是 final 类);
  2. 子类代理:代理对象是真实角色的子类,通过继承实现增强;
  3. Spring 默认选择:Spring 中如果目标对象有接口,用 JDK 动态代理;无接口,用 CGLIB。

五、三大代理模式对比(一张表看懂)

表格

特性

静态代理

JDK 动态代理

CGLIB 动态代理

实现方式

实现同一接口

实现 InvocationHandler

继承目标类 + MethodInterceptor

是否需要接口

代理类创建时机

编译期

运行期

运行期

通用性

差(仅服务一个类)

强(服务所有接口类)

强(服务所有非 final 类)

性能

高(编译期生成)

中(反射调用)

高(直接调用方法)

六、总结 & 面试小彩蛋

  1. 代理模式核心:代理对象控制真实对象的访问,增强核心业务的前后逻辑;
  2. 静态代理:简单易上手,但代码重复、通用性差,适合简单场景;
  3. JDK 动态代理:基于接口,运行时生成代理类,通用性强,Spring AOP 默认选择(有接口时);
  4. CGLIB 动态代理:基于继承,无需接口,性能略优,适合无接口的场景;

面试常问:Spring AOP 用了哪种代理?

  • 如果目标对象实现了接口:默认用 JDK 动态代理;
  • 如果目标对象没实现接口:用 CGLIB 动态代理;
  • 也可以手动配置 Spring 强制使用 CGLIB(设置proxy-target-class="true")。

最后

如果这篇用 “周杰伦演唱会” 讲透代理模式的文章对你有帮助,别忘了点赞 + 收藏~关注我,后续解锁更多 “接地气” 的 Java 设计模式教程!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐