Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 ------ Spring AOP 到底用了哪个?
摘要 :代理模式和装饰器模式是最容易被混淆的结构型模式,它们都持有目标对象的引用,都能在方法调用前后添加逻辑。但Spring AOP 用的是代理模式,Java I/O 流用的是装饰器模式。它们的核心区别是什么?本文从代码、UML、框架源码三个维度深度对比,结合 Spring AOP 的
JdkDynamicAopProxy和BufferedInputStream的源码,帮你彻底分清这对"孪生兄弟"。
🗺️ 本文阅读地图(3 分钟速览)
- 为什么
Collections.synchronizedList()是装饰器?为什么 Spring AOP 是代理?- 代理 vs 装饰器:意图、结构、创建的全面对比
- 手写缓存代理 vs 加密装饰器,看代码感受差异
- Spring AOP 源码级分析:
JdkDynamicAopProxy如何体现代理模式- JDK 中哪些是代理?哪些是装饰器?一张表搞定
- 面试必问:"Spring AOP 到底用的是代理还是装饰器?"
📖 《Java 23 种设计模式:从踩坑到精通》开篇:系列介绍与目录 | 当前:番外 · 代理 vs 装饰器
🔗 返回系列总目录
文章目录
- [Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 ------ Spring AOP 到底用了哪个?](#Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 —— Spring AOP 到底用了哪个?)
-
- [1. 从一个"缓存查询"的需求说起](#1. 从一个“缓存查询”的需求说起)
-
- [1.1 用代理模式实现](#1.1 用代理模式实现)
- [1.2 用装饰器模式实现](#1.2 用装饰器模式实现)
- [1.3 客户端使用的区别](#1.3 客户端使用的区别)
- [2. UML 对比:结构相似,意图不同](#2. UML 对比:结构相似,意图不同)
-
- [2.1 代理模式 UML](#2.1 代理模式 UML)
- [2.2 装饰器模式 UML](#2.2 装饰器模式 UML)
- [3. 三大核心区别](#3. 三大核心区别)
- [4. Spring AOP 用的是代理模式(源码举证)](#4. Spring AOP 用的是代理模式(源码举证))
- [5. JDK 中的代理 vs 装饰器速查](#5. JDK 中的代理 vs 装饰器速查)
- [6. 面试必问 + 追问连环炮](#6. 面试必问 + 追问连环炮)
- [🧭 《Java 23 种设计模式:从踩坑到精通》快速导航](#🧭 《Java 23 种设计模式:从踩坑到精通》快速导航)
1. 从一个"缓存查询"的需求说起
假设你有一个 OrderService,每次调用 queryOrder() 都要查数据库。你想加一个缓存:第一次查数据库,后续直接返回缓存。
1.1 用代理模式实现
java
// 代理类:自己创建真实对象,控制访问
public class OrderServiceProxy implements OrderService {
private OrderService realService = new OrderServiceImpl();
private Map<String, Order> cache = new HashMap<>();
@Override
public Order queryOrder(String orderId) {
if (cache.containsKey(orderId)) {
System.out.println("[代理] 缓存命中");
return cache.get(orderId);
}
System.out.println("[代理] 缓存未命中,调用真实服务");
Order order = realService.queryOrder(orderId);
cache.put(orderId, order);
return order;
}
}
1.2 用装饰器模式实现
java
// 装饰器:由客户端传入被装饰对象,可以层层包裹
public class CachingOrderDecorator implements OrderService {
private OrderService delegate; // 被装饰的对象
private Map<String, Order> cache = new HashMap<>();
public CachingOrderDecorator(OrderService delegate) {
this.delegate = delegate;
}
@Override
public Order queryOrder(String orderId) {
if (cache.containsKey(orderId)) {
System.out.println("[装饰器] 缓存命中");
return cache.get(orderId);
}
System.out.println("[装饰器] 缓存未命中,调用下层");
Order order = delegate.queryOrder(orderId);
cache.put(orderId, order);
return order;
}
}
1.3 客户端使用的区别
java
// 代理:客户端无感知,以为在调真实对象
OrderService proxy = new OrderServiceProxy(); // 代理自己创建了真实对象
proxy.queryOrder("001");
// 装饰器:客户端显式组合
OrderService real = new OrderServiceImpl();
OrderService decorator = new CachingOrderDecorator(real); // 客户端传入
OrderService logDecorator = new LoggingOrderDecorator(decorator); // 层层嵌套
logDecorator.queryOrder("001");
关键差异已浮现:
💡 延伸阅读:本文是代理模式与装饰器模式的深度对比。如果你想先了解每种模式的完整原理、UML、代码实现和面试题,可以先阅读正篇------代理模式:你的 AOP 就是用代理实现的 和 装饰器模式:比继承更灵活的扩展方式,再回来看这篇对比,效果更佳。
2. UML 对比:结构相似,意图不同
2.1 代理模式 UML

2.2 装饰器模式 UML

UML 的关键差异:
- 代理模式的
Proxy通常直接持有RealSubject,并自己创建它; - 装饰器模式的
Decorator持有的是抽象Component,由客户端注入,支持多层嵌套; - 装饰器有一个抽象装饰器层,代理通常没有。
3. 三大核心区别
| 对比维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 意图 | 控制对对象的访问 | 动态增强对象的功能 |
| 真实对象的创建 | 代理自己创建(控制创建时机) | 由客户端创建并注入 |
| 客户端感知 | 客户端通常不知道代理的存在 | 客户端显式组合装饰器 |
| 嵌套能力 | 通常一对一 | 可层层嵌套(俄罗斯套娃) |
| 典型应用 | Spring AOP、MyBatis Mapper、RPC 远程代理 | Java I/O 流、Collections.synchronizedList() |
| 关注点 | 权限、缓存、事务、延迟加载 | 功能叠加、格式转换、加密压缩 |
💡 简单记忆:代理是"中介"------控访问、管创建;装饰器是"加料师"------加功能、可套娃。
4. Spring AOP 用的是代理模式(源码举证)
Spring AOP 的实现类 JdkDynamicAopProxy 是代理模式的教科书级实现:
java
// Spring 源码简化版
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
private final AdvisedSupport advised;
@Override
public Object getProxy() {
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 获取拦截器链(事务、缓存、权限等)
List<Object> chain = advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 2. 没有拦截器,直接调用目标方法
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
// 3. 创建 MethodInvocation,按顺序执行拦截器链
MethodInvocation invocation = new ReflectiveMethodInvocation(
proxy, target, method, args, targetClass, chain);
return invocation.proceed();
}
}
为什么是代理模式?
- 意图是"控制访问":事务管理、权限校验、缓存控制------这些都是"控制",不是"增强功能"。
- 代理自己创建真实对象 :Spring AOP 的代理对象由
ProxyFactory创建,内部持有TargetSource,代理控制着何时创建、如何获取真实对象。 - 客户端无感知 :你注入的
@Autowired OrderService已经是代理对象了,业务代码完全不知道它的存在。 - 不支持层层嵌套 :Spring AOP 的代理通常是一个代理对象包裹一个真实对象,而非像
new BufferedReader(new InputStreamReader(new FileInputStream()))那样层层嵌套。
5. JDK 中的代理 vs 装饰器速查
| JDK 类 | 模式 | 判断依据 |
|---|---|---|
BufferedInputStream |
装饰器 | 包装 InputStream,增加缓冲功能 |
DataInputStream |
装饰器 | 包装 InputStream,增加读取基本类型的功能 |
InputStreamReader |
适配器 | 将字节流接口转换为字符流接口 |
Collections.synchronizedList() |
装饰器 | 包装 List,增加线程安全功能 |
Collections.unmodifiableList() |
代理 | 包装 List,控制访问(禁止修改) |
Proxy.newProxyInstance() |
代理 | 动态生成代理类,控制方法调用 |
Runnable / Callable |
命令 | 封装请求,不是代理也不是装饰器 |
6. 面试必问 + 追问连环炮
基础必问
- Spring AOP 用的是代理还是装饰器?
👉 代理模式。Spring AOP 通过 JDK 动态代理或 CGLIB 生成代理对象,控制对目标对象的访问,织入横切逻辑(事务、缓存、权限)。 - 代理和装饰器的核心区别?
👉 代理"控制访问",装饰器"增强功能"。代理自己创建真实对象,装饰器由客户端注入。
面试官追问
- "
Collections.unmodifiableList()是装饰器还是代理?"
👉 更接近代理。它的主要目的是控制访问(禁止修改),而非增强功能。 - "为什么 Spring AOP 不用装饰器模式?"
👉 装饰器要求客户端显式组合,而 Spring AOP 的核心价值在于对客户端透明------业务代码完全不知道代理的存在。如果用装饰器,每个 Service 都要手动包裹,侵入性太强。 - "Java I/O 流为什么用装饰器而不用代理?"
👉 因为 I/O 流的增强需要灵活组合 ------你可以Buffered+Data+Pushback任意嵌套,装饰器天然支持这种组合,代理不支持。
🎉 恭喜:如果你能立刻说出 Spring AOP = 代理模式、Java I/O = 装饰器模式,并清楚"控制访问 vs 增强功能"的本质区别,你已经掌握了结构型模式中最核心的辨析。
🧭 《Java 23 种设计模式:从踩坑到精通》快速导航
- 开篇:系列介绍与目录
- 当前:番外 · 代理 vs 装饰器 ------ Spring AOP 到底用了哪个?(你在这里)
- 📖 延伸阅读:代理模式正篇 | 装饰器模式正篇
- 创建型模式汇总
- 结构型模式汇总
- 行为型模式汇总
🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战 (WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。