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

张开发
2026/4/6 19:49:47 15 分钟阅读

分享文章

JAVA重点基础、进阶知识及易错点总结(33)设计模式(代理、装饰器)
Java 巩固进阶 · 第 33 天主题设计模式代理、装饰器—— 增强对象功能的两种套路 进度概览继创建型模式单例/工厂/建造者/原型之后今天进入结构型模式。代理模式和装饰器模式是 Java 开发中最常用的对象增强手段更是 Spring AOP 的底层基石。 核心价值框架原理理解 SpringTransactional、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.targettarget;}publicvoidaddUser(){System.out.println(【日志】方法开始);// 前置增强target.addUser();// 调用目标System.out.println(【日志】方法结束);// 后置增强}}// 4. 调用UserServiceproxynewUserServiceProxy(newUserServiceImpl());proxy.addUser();⚠️缺点每个接口都需要写一个代理类代码量大维护成本高。3. 动态代理⭐ 核心// ✅ JDK 动态代理基于接口运行时生成代理类UserServicetargetnewUserServiceImpl();UserServiceproxy(UserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),// 类加载器target.getClass().getInterfaces(),// 实现的接口(proxyObj,method,args)-{// 拦截器核心逻辑System.out.println(【日志】方法开始method.getName());Objectresultmethod.invoke(target,args);// 反射调用目标System.out.println(【日志】方法结束);returnresult;});proxy.addUser();// 自动触发拦截器4. JDK 动态代理 vs CGLIB 代理特性JDK 动态代理CGLIB 动态代理实现方式基于接口实现 InvocationHandler基于继承生成子类依赖JDK 自带java.lang.reflect.Proxy第三方库cglibSpring 核心依赖限制目标类必须实现接口目标类不能有final方法/类性能JDK8 优化后性能较好早期版本较好现在差距不大Spring 选择有接口用 JDK无接口用 CGLIBSpringBoot 2.x 后默认强制 CGLIBSpring AOP 原理Spring 的Transactional本质上就是动态代理扫描 Bean发现注解 → 创建代理对象调用方法时 → 先开启事务 → 再执行目标方法 → 提交/回滚事务二、装饰器模式Decorator动态增强对象功能 1. 为什么需要装饰器 场景需要给对象添加功能但不想用继承继承会导致类爆炸 ❌ 继承问题Coffee Milk → MilkCoffee, Coffee Sugar → SugarCoffee, CoffeeMilkSugar → ... ✅ 装饰器用组合替代继承动态叠加功能2. 核心结构四层角色// 1. 组件接口ComponentinterfaceCoffee{doublegetCost();StringgetDescription();}// 2. 具体组件ConcreteComponentclassSimpleCoffeeimplementsCoffee{publicdoublegetCost(){return10.0;}publicStringgetDescription(){return普通咖啡;}}// 3. 装饰器基类Decorator→ 持有组件接口abstractclassCoffeeDecoratorimplementsCoffee{protectedCoffeedecoratedCoffee;// ✅ 核心组合关系publicCoffeeDecorator(Coffeecoffee){this.decoratedCoffeecoffee;}publicdoublegetCost(){returndecoratedCoffee.getCost();}publicStringgetDescription(){returndecoratedCoffee.getDescription();}}// 4. 具体装饰器ConcreteDecoratorclassMilkDecoratorextendsCoffeeDecorator{publicMilkDecorator(Coffeecoffee){super(coffee);}publicdoublegetCost(){returnsuper.getCost()2.0;}// 增强功能publicStringgetDescription(){returnsuper.getDescription() 牛奶;}}// 调用CoffeecoffeenewSimpleCoffee();coffeenewMilkDecorator(coffee);// 动态添加牛奶System.out.println(coffee.getDescription());// 普通咖啡 牛奶3. 经典案例Java IO 流// ✅ IO 流是装饰器模式的典型应用InputStreaminnewFileInputStream(a.txt);// 原始流innewBufferedInputStream(in);// 装饰加缓冲innewDataInputStream(in);// 装饰加数据类型读取// 层层包装功能叠加但不改变原始对象类型三、代理模式 vs 装饰器模式区别在哪 对比项代理模式Proxy装饰器模式Decorator意图控制访问添加额外逻辑如事务/日志增强功能添加新行为如加牛奶/缓冲关注点调用方无感知透明调用方知道被装饰可叠加关系代理类通常由框架生成装饰器通常手动组合典型应用Spring AOP、RPC 客户端Java IO 流、Servlet Request/Response 包装一句话区分“代理是’替身’重点在控制装饰器是’外套’重点在增强”四、 今日实战任务模式综合应用任务 1实现静态代理/** * 要求 * 1. 定义 OrderService 接口createOrder 方法 * 2. 实现类 OrderServiceImpl * 3. 代理类 OrderServiceProxy在创建订单前后打印日志 * 4. 测试调用代理对象观察日志输出 * * 提示 * 代理类需实现同一接口持有目标对象引用 */任务 2JDK 动态代理实现日志切面⭐ 核心/** * 业务场景为任意 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 天 · 核心总结极简背诵版代理模式核心// 静态代理手动写代理类// 动态代理Proxy.newProxyInstance(loader, interfaces, handler)// 用途Spring AOP、事务控制、日志记录JDK 动态代理 vs CGLIBJDK基于接口无需第三方库 CGLIB基于继承可代理无接口类Spring 默认装饰器模式核心// 组合替代继承层层包装newBufferedInputStream(newFileInputStream(...))// 用途IO 流、Servlet 包装、功能动态叠加代理 vs 装饰器代理控制访问替身调用方无感知 装饰器增强功能外套调用方主动包装明天预告️注解基础Annotation—— 代码的元数据注解的定义interface与元注解Target/Retention内置注解Override/Deprecated自定义注解实战实战定义一个「日志注解」Log准备好了吗明天我们学习 Java 的标签系统 ✨

更多文章