Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 —— Spring AOP 到底用了哪个?

Java 23 种设计模式:从踩坑到精通 | 番外:代理 vs 装饰器 ------ Spring AOP 到底用了哪个?

摘要 :代理模式和装饰器模式是最容易被混淆的结构型模式,它们都持有目标对象的引用,都能在方法调用前后添加逻辑。但Spring AOP 用的是代理模式,Java I/O 流用的是装饰器模式。它们的核心区别是什么?本文从代码、UML、框架源码三个维度深度对比,结合 Spring AOP 的 JdkDynamicAopProxyBufferedInputStream 的源码,帮你彻底分清这对"孪生兄弟"。
🗺️ 本文阅读地图(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();
    }
}

为什么是代理模式?

  1. 意图是"控制访问":事务管理、权限校验、缓存控制------这些都是"控制",不是"增强功能"。
  2. 代理自己创建真实对象 :Spring AOP 的代理对象由 ProxyFactory 创建,内部持有 TargetSource,代理控制着何时创建、如何获取真实对象。
  3. 客户端无感知 :你注入的 @Autowired OrderService 已经是代理对象了,业务代码完全不知道它的存在。
  4. 不支持层层嵌套 :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 种设计模式:从踩坑到精通》快速导航

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。

📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
📌 除了设计模式,我也在深挖智能物流实战 (WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。

相关推荐
雪碧聊技术1 小时前
一文深入理解 Java 函数式编程思想
java·函数式编程
AIGS0011 小时前
突破语义鸿沟:从向量空间JBoltAI看企业大脑构建逻辑
java·大数据·人工智能·人工智能ai大模型应用
琢磨先生David2 小时前
OpenJDK 与 Oracle JDK 完整区别
java
倔强的石头1062 小时前
企业智能设备运维管理系统:靠飞算 JavaAI,告别 Java 低效搬砖日常
java
深盾科技_Virbox2 小时前
软件授权工具静默安装实践
java·运维·数据库·安全·软件需求
qq_452396232 小时前
第二篇:《Jenkins 从零搭建:安装、配置与第一个 Pipeline》
java·运维·jenkins
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第172题】【07_Redis篇】第8题:`setnx` 做分布式锁存在的问题
java·redis·分布式锁·死锁·setnx
喜欢打篮球的普通人3 小时前
LLVM后端指令选择
android·java·javascript
某不知名網友3 小时前
C++ 七大排序算法完整讲解
java·算法·排序算法