本文 5000 字深度原创含完整代码示例和生产级落地方案。创作不易如果对你有帮助请点赞 收藏 ⭐ 关注 三连支持你的认可是我持续输出的最大动力本文是「设计模式实战解读」系列第六篇。系列文章统一按照定义 → 痛点场景 → 模式结构 → 核心实现 → 真实应用 → 常见变种 → 优缺点 → 避坑指南 → FAQ的结构展开每篇聚焦一个模式讲透。一句话定义装饰器模式Decorator在不修改原始对象、不使用子类继承的前提下通过包装的方式动态地给对象添加新功能。归属结构型模式。一、没有装饰器时的痛点假设你有一个MessageSender接口基础实现是HttpMessageSender。现在要给它加三个功能日志记录、失败重试、执行耗时统计。方案一直接修改原类publicclassHttpMessageSenderimplementsMessageSender{publicvoidsend(Messagemsg){longstartSystem.currentTimeMillis();// 加了耗时统计intretryCount0;while(retryCount3){// 加了重试try{log.info(发送消息: {},msg.getId());// 加了日志// 原来的逻辑...httpClient.post(url,msg);log.info(发送成功: {},msg.getId());return;}catch(Exceptione){retryCount;log.warn(第{}次重试,retryCount);}}longcostSystem.currentTimeMillis()-start;// 耗时metricsService.record(message.send.cost,cost);}}问题原来只有 5 行的核心逻辑被日志/重试/监控的代码淹没了。而且这三个能力是耦合在一起的——如果另一个MqMessageSender也需要重试要重复一遍。方案二继承扩展classLoggableMessageSenderextendsHttpMessageSender{...}classRetryableMessageSenderextendsHttpMessageSender{...}classMonitoredMessageSenderextendsHttpMessageSender{...}// 需要同时具备三个能力classLoggableRetryableMonitoredMessageSenderextends???// 爆炸...4 种增强组合理论上需要 2³ 8 个子类。这就是继承爆炸——用继承做功能叠加类的数量以指数级增长。核心诉求把增强能力日志/重试/监控/缓存/鉴权和核心能力分离按需组合不修改原始类。二、模式结构┌─────────────────────────────┐ │ Component组件接口 │ │ send(Message): void │ ← 统一接口 └─────────────┬───────────────┘ │ ┌──────────┴──────────┐ │ │ ↓ ↓ ┌──────────────┐ ┌──────────────────────────┐ │ Concrete │ │ Decorator抽象装饰器 │ │ Component │ ├──────────────────────────┤ │ (真实实现) │ │ - wrapped: Component │ ← 持有被装饰对象 │ │ ├──────────────────────────┤ │HttpSender │ │ send(Message) │ ← 调用 wrapped.send() └──────────────┘ └────────────┬─────────────┘ │ 继承 ┌────────────┼──────────────┐ ↓ ↓ ↓ LogDecorator RetryDecorator MonitorDecorator装饰器和被装饰对象实现同一个接口——这是关键使得装饰器可以嵌套// 装饰器可以像俄罗斯套娃一样叠加MessageSendersendernewHttpMessageSender();// 原始对象sendernewLogDecorator(sender);// 加日志sendernewRetryDecorator(sender,3);// 再加重试sendernewMonitorDecorator(sender,metricsService);// 再加监控sender.send(msg);// 触发时监控 → 重试 → 日志 → 实际发送三、核心实现3.1 接口 抽象装饰器// 组件接口publicinterfaceMessageSender{voidsend(Messagemessage);}// 具体组件核心逻辑保持干净publicclassHttpMessageSenderimplementsMessageSender{privatefinalStringurl;publicHttpMessageSender(Stringurl){this.urlurl;}Overridepublicvoidsend(Messagemessage){// 只做一件事发送 HTTP 请求httpClient.post(url,message.toJson());}}// 抽象装饰器持有被装饰对象转发调用publicabstractclassMessageSenderDecoratorimplementsMessageSender{protectedfinalMessageSenderwrapped;// 被装饰的对象publicMessageSenderDecorator(MessageSenderwrapped){this.wrappedwrapped;}Overridepublicvoidsend(Messagemessage){wrapped.send(message);// 默认直接转发}}3.2 具体装饰器// 装饰器1日志装饰publicclassLogDecoratorextendsMessageSenderDecorator{publicLogDecorator(MessageSenderwrapped){super(wrapped);}Overridepublicvoidsend(Messagemessage){log.info([MessageSender] 开始发送, messageId{},message.getId());try{wrapped.send(message);// 调用被装饰对象log.info([MessageSender] 发送成功, messageId{},message.getId());}catch(Exceptione){log.error([MessageSender] 发送失败, messageId{},message.getId(),e);throwe;}}}// 装饰器2重试装饰publicclassRetryDecoratorextendsMessageSenderDecorator{privatefinalintmaxRetry;publicRetryDecorator(MessageSenderwrapped,intmaxRetry){super(wrapped);this.maxRetrymaxRetry;}Overridepublicvoidsend(Messagemessage){intattempt0;ExceptionlastExceptionnull;while(attemptmaxRetry){try{wrapped.send(message);return;// 成功即返回}catch(Exceptione){lastExceptione;attempt;if(attemptmaxRetry){log.warn(第 {} 次重试, messageId{},attempt,message.getId());sleepQuietly(attempt*500L);// 退避等待}}}thrownewRuntimeException(重试 maxRetry 次后仍失败,lastException);}}// 装饰器3监控装饰publicclassMonitorDecoratorextendsMessageSenderDecorator{privatefinalMetricsServicemetricsService;publicMonitorDecorator(MessageSenderwrapped,MetricsServicemetricsService){super(wrapped);this.metricsServicemetricsService;}Overridepublicvoidsend(Messagemessage){longstartSystem.currentTimeMillis();try{wrapped.send(message);metricsService.increment(message.send.success);}catch(Exceptione){metricsService.increment(message.send.failure);throwe;}finally{longcostSystem.currentTimeMillis()-start;metricsService.recordTime(message.send.cost,cost);}}}3.3 装饰器组合使用// 按需自由组合顺序可以调整MessageSendersendernewHttpMessageSender(https://api.example.com/send);sendernewRetryDecorator(sender,3);// 先重试内层sendernewLogDecorator(sender);// 再记日志中层sendernewMonitorDecorator(sender,metricsService);// 最外层监控sender.send(message);// 执行顺序监控.before → 日志.before → 重试.before → HttpSender → 重试.after → 日志.after → 监控.after3.4 Spring 中的 Builder 风格// 提供流式 Builder让组合更直观publicclassMessageSenderBuilder{privateMessageSendersender;publicstaticMessageSenderBuilderwrap(MessageSendersender){MessageSenderBuilderbuildernewMessageSenderBuilder();builder.sendersender;returnbuilder;}publicMessageSenderBuilderwithLog(){sendernewLogDecorator(sender);returnthis;}publicMessageSenderBuilderwithRetry(intmaxRetry){sendernewRetryDecorator(sender,maxRetry);returnthis;}publicMessageSenderBuilderwithMonitor(MetricsServicemetrics){sendernewMonitorDecorator(sender,metrics);returnthis;}publicMessageSenderbuild(){returnsender;}}// 使用MessageSendersenderMessageSenderBuilder.wrap(newHttpMessageSender(url)).withRetry(3).withLog().withMonitor(metricsService).build();四、真实应用场景4.1 框架级应用——Java I/O 流最经典的装饰器// Java IO 就是装饰器模式的教科书示例InputStreamrawnewFileInputStream(data.csv);// 基础文件流InputStreambufferednewBufferedInputStream(raw);// 装饰加缓冲InputStreamunzippednewGZIPInputStream(buffered);// 装饰加解压ReaderreadernewInputStreamReader(unzipped,UTF-8);// 装饰字节转字符// 每层只做自己的增强不关心其他层组件角色对应类组件接口InputStream具体组件FileInputStream / ByteArrayInputStream抽象装饰器FilterInputStream具体装饰器BufferedInputStream / GZIPInputStream / CipherInputStream4.2 框架级应用——Spring 的装饰器场景被装饰对象装饰器增强内容Spring SecurityHttpServletRequestSecurityContextHolderAwareRequestWrapper注入安全上下文Spring CacheRepository 方法CacheInterceptorAOP 实现装饰器思想加缓存Spring TransactionService 方法TransactionInterceptor加事务HttpClientRestTemplate各种 Interceptor加日志/加鉴权/加重试MyBatisExecutorCachingExecutor加二级缓存4.3 业务场景场景核心能力常见装饰接口调用HTTP Client日志 重试 超时 熔断数据访问Repository缓存 读写分离 数据脱敏文件处理FileProcessor加密 压缩 格式校验消息发送MessageSender日志 重试 频控权限校验ResourceHandler鉴权 数据权限过滤4.4 iPaaS 连接器的装饰器链在 iPaaS 平台中连接器调用需要多个横切关注点——这是装饰器的理想场景ConnectorHandler核心构建请求 发送 解析响应 ↑ 被装饰 AuthDecorator → 在请求头注入鉴权信息OAuth / APIKey / Basic ↑ 被装饰 RetryDecorator → 失败自动重试最多3次指数退避 ↑ 被装饰 TimeoutDecorator → 超时控制默认30秒可按连接器配置 ↑ 被装饰 LogDecorator → 记录请求/响应完整日志到执行流水 ↑ 被装饰 MetricsDecorator → 统计调用次数/耗时/成功率每个连接器共用同一套装饰链。新增一个全局能力如请求签名验证只需加一层装饰器所有连接器自动获得不需要改任何连接器代码。// 装饰链构建按连接器配置按需启用ConnectorHandlerhandlernewDingTalkConnectorHandler(config);if(config.isAuthEnabled()){handlernewAuthDecorator(handler,authStrategyFactory.create(config));}handlernewTimeoutDecorator(handler,config.getTimeoutMs());handlernewRetryDecorator(handler,config.getMaxRetry());handlernewLogDecorator(handler,executionLogger);handlernewMetricsDecorator(handler,metricsService);handler.execute(request);// 完整的装饰链执行五、常见变种5.1 函数式装饰Java 8用Function.andThen()/Function.compose()实现函数级装饰// 原始数据处理函数FunctionString,Stringprocesstext-text.trim().toLowerCase();// 装饰加日志FunctionString,StringwithLogprocess.andThen(result-{log.info(处理结果: {},result);returnresult;});// 装饰加脱敏FunctionString,StringwithDesensitizewithLog.andThen(result-result.replaceAll(\\d{4}(?\\d{4}),****));// 使用StringoutputwithDesensitize.apply( 138 1234 5678 );// 输出1381****56785.2 AOP 与装饰器的关系Spring AOP 和装饰器都能实现不修改原代码添加横切功能区别在于维度装饰器模式Spring AOP时机运行时手动组合编译/运行时自动代理控制粒度对象级别哪个实例切点哪些方法透明度调用方感知知道包了几层调用方无感透明代理适用场景需要灵活组合不同装饰全局横切、声明式Cacheable/Transactional性能无代理开销有动态代理开销使用建议少量固定增强全局日志、全局事务→ 用 AOP需要动态组合、对象级控制 → 用装饰器5.3 可配置装饰器链通过配置决定加哪些装饰器// 读取连接器配置动态构建装饰链ConnectorHandlerbuildChain(ConnectorConfigconfig,ConnectorHandlerbase){ConnectorHandlerhandlerbase;// 按配置顺序从内到外加装饰for(Stringfeature:config.getEnabledFeatures()){handlerswitch(feature){caseauth-newAuthDecorator(handler,...);caseretry-newRetryDecorator(handler,config.getMaxRetry());casetimeout-newTimeoutDecorator(handler,config.getTimeoutMs());caselog-newLogDecorator(handler,logger);default-handler;// 未知特性忽略};}returnhandler;}六、优缺点优点缺点不修改原类符合开闭原则多层装饰后调试困难堆栈深装饰可以自由组合装饰顺序有影响需要明确文档化单个装饰器职责单一易测试类数量增加每个能力一个装饰类运行时动态添加/去除装饰使用者需要理解装饰链的构建方式比继承更灵活避免子类爆炸装饰器之间共享状态难以处理七、避坑指南坑 1装饰器持有的 wrapped 被修改装饰器一旦构建不要在运行时替换wrapped——这会导致并发问题。如果需要动态切换应该重新构建整个装饰链。坑 2装饰顺序搞错装饰顺序不同行为不同。以RetryDecorator LogDecorator为例情况ALog(Retry(HttpSender)) → 记录 开始发送 → [重试机制] → HTTP发送 → 记录 成功/失败 → 日志只记录最终结果重试细节不记 情况BRetry(Log(HttpSender)) → [重试机制] → 记录 开始发送 → HTTP发送 → 记录 成功/失败 → [重试] → 每次重试都会记录日志重试几次记几条原则越靠近内层的装饰器越先执行业务逻辑越靠近外层的装饰器越先拦截。建议用注释或文档明确装饰顺序和原因。坑 3装饰器不应该改变接口行为装饰器只能增强不能改变原来的语义。比如// ❌ 错误装饰器改变了返回值语义publicclassUpperCaseDecoratorextendsMessageSenderDecorator{Overridepublicvoidsend(Messagemessage){// 把消息内容改成大写再发送——这不是增强是篡改message.setContent(message.getContent().toUpperCase());wrapped.send(message);}}数据转换这类操作不是装饰应该在业务层做或者用职责链/管道模式处理。坑 4装饰层太深异常堆栈看不懂Exception in thread main ... at MonitorDecorator.send(MonitorDecorator.java:28) at LogDecorator.send(LogDecorator.java:15) at RetryDecorator.send(RetryDecorator.java:22) at AuthDecorator.send(AuthDecorator.java:19) at HttpMessageSender.send(HttpMessageSender.java:45)缓解方案控制装饰层数≤5层在最外层装饰器统一打印完整上下文信息每个装饰器的异常消息加上自身标识[RetryDecorator] 重试耗尽坑 5用装饰器实现业务分支// ❌ 用装饰器实现 if-else应该用策略模式publicclassVipDiscountDecoratorextendsPriceCalculatorDecorator{OverridepublicBigDecimalcalculate(Orderorder){BigDecimalpricewrapped.calculate(order);if(order.isVip()){returnprice.multiply(newBigDecimal(0.9));// VIP 打九折}returnprice;// 非 VIP 不打折}}装饰器内有业务分支 → 应该用策略模式而不是装饰器。装饰器的职责是无条件增强不是有条件执行不同逻辑。八、常见问题FAQQ装饰器模式和代理模式有什么区别A两者结构几乎相同都是包装同一个接口区别在于意图装饰器增强对象的功能调用方明确知道在使用装饰后的对象关注功能扩展代理控制对象的访问调用方以为在直接使用原对象关注访问控制懒加载、权限校验、远程代理实际使用中边界模糊Spring AOP 的 Proxy 就兼具两者的特点。Q什么时候用装饰器什么时候用继承A继承是编译时固定的装饰器是运行时动态的。如果增强能力的组合是固定的永远都要有日志和监控可以用继承如果增强能力需要按需组合有时要重试有时不要有时要加密有时不要用装饰器。Q多个装饰器叠加性能有影响吗A每层装饰增加一次方法调用JVM 的 JIT 编译器在大多数情况下会内联优化尤其是调用链固定时实际性能损耗可以忽略不计。只有在极高频率调用每秒百万级且装饰器本身逻辑非常简单时才需要考虑。QJava I/O 的装饰器为什么被批评AJava I/O 的装饰器实现确实不够友好——需要手动层层包装new BufferedInputStream(new GZIPInputStream(new FileInputStream(...)))创建时语法繁琐且装饰顺序容易出错。现代 API 设计如 Builder 模式 流式 API可以改善这个问题。QSpring 的Cacheable是装饰器模式吗A从思想上是的——在不修改原方法的前提下给它加上缓存能力。但实现上 Spring 用的是 AOP动态代理不是手工编写的装饰类。可以理解为用 AOP 实现的自动装饰器。九、小结装饰器模式的核心价值把需要什么能力和核心逻辑是什么分离通过包装实现按需组合。三个实践要点装饰器和被装饰对象必须实现同一个接口——这是装饰器可以嵌套的前提装饰器只做增强不改变语义——有业务分支的增强逻辑用策略模式控制装饰层数≤5层并文档化顺序——装饰链过深时调试是噩梦设计模式系列前六篇单例/工厂/模板方法/观察者/策略/装饰器已形成完整的创建型 行为型 结构型覆盖后续可继续扩展责任链、代理、组合模式。标签#设计模式 #装饰器模式 #Decorator #结构型模式 #Java #JavaIO #Spring #AOP #连接器增强 #函数式编程 #代理模式 #面向对象 #软件工程