当前位置: 首页 > news >正文

JAVA重点基础、进阶知识及易错点总结(33)设计模式(代理、装饰器)

🚀 Java 巩固进阶 · 第 33 天

主题:设计模式(代理、装饰器)—— 增强对象功能的两种套路

📅 进度概览:继创建型模式(单例/工厂/建造者/原型)之后,今天进入结构型模式。代理模式和装饰器模式是 Java 开发中最常用的"对象增强"手段,更是 Spring AOP 的底层基石。

💡 核心价值

  • 框架原理:理解 Spring@Transactional@Async、MyBatis Mapper 的底层代理机制。
  • 代码增强:在不修改原有代码的基础上,动态添加日志、事务、权限校验等功能。
  • 面试高频:JDK 动态代理 vs CGLIB、代理 vs 装饰器区别,是高级开发必考题。
  • 设计思维:学会"组合优于继承",用包装思想灵活扩展功能。

一、代理模式(Proxy):控制访问与增强逻辑 🎭

1. 为什么需要代理?

🎯 场景:想在调用目标方法前后添加逻辑(如日志、事务),但不想修改目标类代码 ❌ 普通做法:在每个方法里手动写 log.info() → 代码重复,侵入性强 ✅ 代理模式:创建代理对象,包裹目标对象,在调用前后插入逻辑

2. 静态代理(基础)

// 1. 定义接口interfaceUserService{voidaddUser();}// 2. 目标类classUserServiceImplimplementsUserService{publicvoidaddUser(){System.out.println("添加用户...");}}// 3. 代理类(手动编写,持有目标对象)classUserServiceProxyimplementsUserService{privateUserServicetarget;publicUserServiceProxy(UserServicetarget){this.target=target;}publicvoidaddUser(){System.out.println("【日志】方法开始");// 前置增强target.addUser();// 调用目标System.out.println("【日志】方法结束");// 后置增强}}// 4. 调用UserServiceproxy=newUserServiceProxy(newUserServiceImpl());proxy.addUser();

⚠️缺点:每个接口都需要写一个代理类,代码量大,维护成本高。

3. 动态代理(⭐ 核心)

// ✅ JDK 动态代理(基于接口,运行时生成代理类)UserServicetarget=newUserServiceImpl();UserServiceproxy=(UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),// 类加载器target.getClass().getInterfaces(),// 实现的接口(proxyObj,method,args)->{// 拦截器(核心逻辑)System.out.println("【日志】方法开始:"+method.getName());Objectresult=method.invoke(target,args);// 反射调用目标System.out.println("【日志】方法结束");returnresult;});proxy.addUser();// 自动触发拦截器

4. JDK 动态代理 vs CGLIB 代理

特性JDK 动态代理CGLIB 动态代理
实现方式基于接口(实现 InvocationHandler)基于继承(生成子类)
依赖JDK 自带(java.lang.reflect.Proxy第三方库(cglib,Spring 核心依赖)
限制目标类必须实现接口目标类不能有final方法/类
性能JDK8+ 优化后性能较好早期版本较好,现在差距不大
Spring 选择有接口用 JDK,无接口用 CGLIBSpringBoot 2.x 后默认强制 CGLIB

💡Spring AOP 原理
Spring 的@Transactional本质上就是动态代理:

  1. 扫描 Bean
  2. 发现注解 → 创建代理对象
  3. 调用方法时 → 先开启事务 → 再执行目标方法 → 提交/回滚事务

二、装饰器模式(Decorator):动态增强对象功能 🎨

1. 为什么需要装饰器?

🎯 场景:需要给对象添加功能,但不想用继承(继承会导致类爆炸) ❌ 继承问题:Coffee + Milk → MilkCoffee, Coffee + Sugar → SugarCoffee, Coffee+Milk+Sugar → ... ✅ 装饰器:用组合替代继承,动态叠加功能

2. 核心结构(四层角色)

// 1. 组件接口(Component)interfaceCoffee{doublegetCost();StringgetDescription();}// 2. 具体组件(ConcreteComponent)classSimpleCoffeeimplementsCoffee{publicdoublegetCost(){return10.0;}publicStringgetDescription(){return"普通咖啡";}}// 3. 装饰器基类(Decorator)→ 持有组件接口abstractclassCoffeeDecoratorimplementsCoffee{protectedCoffeedecoratedCoffee;// ✅ 核心:组合关系publicCoffeeDecorator(Coffeecoffee){this.decoratedCoffee=coffee;}publicdoublegetCost(){returndecoratedCoffee.getCost();}publicStringgetDescription(){returndecoratedCoffee.getDescription();}}// 4. 具体装饰器(ConcreteDecorator)classMilkDecoratorextendsCoffeeDecorator{publicMilkDecorator(Coffeecoffee){super(coffee);}publicdoublegetCost(){returnsuper.getCost()+2.0;}// 增强功能publicStringgetDescription(){returnsuper.getDescription()+"+ 牛奶";}}// 调用Coffeecoffee=newSimpleCoffee();coffee=newMilkDecorator(coffee);// 动态添加牛奶System.out.println(coffee.getDescription());// "普通咖啡 + 牛奶"

3. 🔍 经典案例:Java IO 流

// ✅ IO 流是装饰器模式的典型应用InputStreamin=newFileInputStream("a.txt");// 原始流in=newBufferedInputStream(in);// 装饰:加缓冲in=newDataInputStream(in);// 装饰:加数据类型读取// 层层包装,功能叠加,但不改变原始对象类型

三、代理模式 vs 装饰器模式:区别在哪? 🤔

对比项代理模式(Proxy)装饰器模式(Decorator)
意图控制访问,添加额外逻辑(如事务/日志)增强功能,添加新行为(如加牛奶/缓冲)
关注点调用方无感知(透明)调用方知道被装饰(可叠加)
关系代理类通常由框架生成装饰器通常手动组合
典型应用Spring AOP、RPC 客户端Java IO 流、Servlet Request/Response 包装

💡一句话区分
“代理是’替身’,重点在控制;装饰器是’外套’,重点在增强”


四、🎯 今日实战任务:模式综合应用

任务 1:实现静态代理

/** * 要求: * 1. 定义 OrderService 接口(createOrder 方法) * 2. 实现类 OrderServiceImpl * 3. 代理类 OrderServiceProxy,在创建订单前后打印日志 * 4. 测试:调用代理对象,观察日志输出 * * 💡 提示: * 代理类需实现同一接口,持有目标对象引用 */

任务 2:JDK 动态代理实现日志切面(⭐ 核心)

/** * 业务场景:为任意 Service 添加日志功能 * * 要求: * 1. 创建 ProxyFactory 工具类 * 2. 实现 getProxy(Object target) 方法,返回代理对象 * 3. 在 InvocationHandler 中记录:方法名、参数、执行耗时 * 4. 测试:传入 UserServiceImpl,调用方法,观察日志 * * 💡 挑战: * - 如何处理方法抛出的异常?(需在 invoke 中 catch 并记录) * - 如何获取方法执行耗时?(System.currentTimeMillis() 前后相减) */publicclassProxyFactory{publicstaticObjectgetProxy(Objecttarget){// TODO: 实现 Proxy.newProxyInstance 逻辑}}

任务 3:装饰器模式模拟"短信发送"

/** * 业务场景:发送短信,可叠加"加密"、"限流"、"日志"功能 * * 要求: * 1. 定义 MessageSender 接口(send 方法) * 2. 实现类 SmsSender(基础发送) * 3. 装饰器基类 MessageSenderDecorator * 4. 具体装饰器:EncryptionDecorator(加密)、RateLimitDecorator(限流) * 5. 测试:new EncryptionDecorator(new RateLimitDecorator(new SmsSender())) * * 💡 思考: * - 装饰顺序是否影响结果?(如先加密还是先限流) */

任务 4:对比实验:代理 vs 装饰器

/** * 要求: * 1. 用代理模式为 UserService 添加日志 * 2. 用装饰器模式为 UserService 添加日志 * 3. 对比代码结构、调用方式、意图差异 * * 💡 预期结论: * - 代理:调用方无感知,重点在控制访问 * - 装饰器:调用方主动包装,重点在功能叠加 */

📝 第 33 天 · 核心总结(极简背诵版)

  1. 代理模式核心

    // 静态代理:手动写代理类// 动态代理:Proxy.newProxyInstance(loader, interfaces, handler)// 用途:Spring AOP、事务控制、日志记录
  2. JDK 动态代理 vs CGLIB

    JDK:基于接口,无需第三方库 CGLIB:基于继承,可代理无接口类(Spring 默认)
  3. 装饰器模式核心

    // 组合替代继承,层层包装newBufferedInputStream(newFileInputStream(...))// 用途:IO 流、Servlet 包装、功能动态叠加
  4. 代理 vs 装饰器

    代理:控制访问(替身),调用方无感知 装饰器:增强功能(外套),调用方主动包装

明天预告:🏷️注解基础(Annotation)—— 代码的"元数据"!

  • 注解的定义(@interface)与元注解(@Target/@Retention)
  • 内置注解(@Override/@Deprecated)
  • 自定义注解实战
  • 实战:定义一个「日志注解」@Log

准备好了吗?明天我们学习 Java 的"标签系统"! ✨📝

http://www.jsqmd.com/news/598477/

相关文章:

  • 开源工具ncmdump:3步解锁加密音乐,让网易云NCM文件在全设备自由播放
  • Qwen2.5-7B+vLLM离线推理实战:解决V100显卡兼容性问题
  • 避坑指南:RK3588以太网调试那些‘看起来通了但实际没通’的坑(附千兆网线识别与ifconfig -a用法)
  • 3步解锁QQ音乐加密文件:qmc-decoder让音乐真正属于你
  • 软测学习笔记|2026.4.4|错误推测法|因果图(2)
  • 路径规划算法仿真:A星算法详解及改进研究,包括效率提升、冗余拐角优化与路径平滑处理,可量化对比...
  • CNN卷积神经网络算法原理
  • 永磁同步电机无位置传感器矢量控制系统功能详解
  • 开源工具Wand-Enhancer:WeMod核心功能免费解锁与安全使用指南
  • 用Python+OpenCV重构九点标定:抛弃Halcon的轻量化视觉方案
  • 你的第一个Todo List项目藏着这些坑:HTML+CSS+JS新手避雷指南
  • 告别ifconfig手忙脚乱:MobaXterm一键SSH连接VMware Ubuntu的保姆级图解
  • LangChain 官方出手了:这个 Agent 框架自带规划、文件系统和子 Agent 派发
  • StructBERT情感分析镜像部署实录:解决WebUI打不开/超时常见问题
  • 市政规划许可场景钓鱼攻击机理与闭环防御研究
  • 告别网络抽风!Ubuntu 22.04下Intel I219-V网卡设置固定IP与禁用IPv6的保姆级教程
  • 电子取证必备:手把手教你用ADB命令提取手机APK(含避坑指南)
  • Java、从零开始学异常
  • FinalBurn Neo终极指南:3步快速开启你的复古街机游戏之旅
  • PHP防止Shell命令注入的有效方法
  • 无片外电容LDO电路设计 完整IP现成电路,具有过温保护和过流保护,带隙,BUFFER都有 性...
  • 告别手动编译!用SDKManager一键为Jetson Orin NX刷入JetPack 6.2.1并开启实时内核
  • 【实战指南】迪文屏开发全流程解析与优化技巧
  • Florence-2 视觉语言模型适配 Neuron SDK 全记录:Stage-wise 编译与 Bucket 策略实战
  • 终极方案:如何用代码替代拖拽,高效绘制专业架构图与流程图
  • 告别固定阈值!用DBnet做文本检测,手把手教你搞定自适应二值化(附PyTorch代码)
  • 如何快速配置虚拟手柄驱动:面向游戏玩家的完整教程
  • 低代码技术如何重构钣金工厂的数字化生产链路
  • 深入MAX30102传感器:从光电信号到心率血氧值的完整数据处理流程解析
  • 智慧机场三维空间智能中枢系统白皮书——构建“全域感知 × 空间认知 × 智能调度”的下一代机场操作平台