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

代理模式详解:代理、策略与模板方法模式

引言

设计模式是面向对象编程中的经典解决方案,它们封装了前人的经验,提供了可复用的设计思路。本文将重点介绍三种常用的设计模式:代理模式(含静态代理、JDK动态代理、CGLIB代理)、策略模式模板方法模式,并通过代码示例和原理分析帮助读者理解其应用场景和实现方式。

一、代理模式(Proxy Pattern)

1.1 定义与应用场景

代理模式为其他对象提供一种代理以控制对这个对象的访问。核心作用是在不修改目标对象的前提下,通过引入代理对象增强目标对象的功能(如日志记录、事务管理、权限控制等)。

典型应用场景

  • Spring AOP的方法增强
  • RPC框架的服务调用
  • 延迟加载(如Hibernate的懒加载)
  • 权限校验与日志记录

1.2 静态代理

实现原理

静态代理在编译期手动创建代理类,代理类与目标类实现同一接口,并持有目标对象的引用,在调用目标方法前后添加增强逻辑。

代码示例:日志代理
// 1. 定义服务接口
public interface IService {void serve();
}// 2. 实现目标类
public class RealService implements IService {@Overridepublic void serve() {System.out.println("真实服务: 处理核心业务逻辑");}
}// 3. 创建静态代理类
public class StaticProxy implements IService {private final IService target; // 持有目标对象引用public StaticProxy(IService target) {this.target = target;}@Overridepublic void serve() {// 增强逻辑:方法调用前System.out.println("[静态代理] 记录调用开始时间");// 调用目标方法target.serve();// 增强逻辑:方法调用后System.out.println("[静态代理] 记录调用结束时间");}
}// 4. 客户端调用
public class Client {public static void main(String[] args) {IService realService = new RealService();IService proxy = new StaticProxy(realService);proxy.serve();}
}
输出结果:
[静态代理] 记录调用开始时间
真实服务: 处理核心业务逻辑
[静态代理] 记录调用结束时间
优缺点
  • 优点:实现简单,性能较高(编译期确定代理类)。
  • 缺点
    • 代码冗余:每个目标类需对应一个代理类
    • 维护成本高:接口变更时需同步修改代理类

1.3 JDK动态代理

实现原理

JDK动态代理通过反射机制在运行时动态生成代理类,无需手动编写代理类。核心类为java.lang.reflect.ProxyInvocationHandler接口。

  • Proxy类:生成代理对象的工厂类,通过newProxyInstance()方法创建代理实例。
  • InvocationHandler接口:定义代理逻辑的接口,需实现invoke()方法处理增强逻辑。
代码示例:动态日志代理
// 1. 目标接口与实现类(复用静态代理的IService和RealService)// 2. 实现InvocationHandler
public class LogInvocationHandler implements InvocationHandler {private final Object target; // 目标对象public LogInvocationHandler(Object target) {this.target = target;}// 代理逻辑:所有方法调用都会转发到invoke()@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("[JDK动态代理] 方法" + method.getName() + "调用开始");Object result = method.invoke(target, args); // 反射调用目标方法System.out.println("[JDK动态代理] 方法" + method.getName() + "调用结束");return result;}// 创建代理对象public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), // 类加载器target.getClass().getInterfaces(),   // 目标类实现的接口this                                // InvocationHandler实例);}
}// 3. 客户端调用
public class Client {public static void main(String[] args) {IService realService = new RealService();IService proxy = (IService) new LogInvocationHandler(realService).getProxy();proxy.serve();}
}
底层原理分析
  1. 代理类生成Proxy.newProxyInstance()通过ProxyGenerator生成代理类字节码,类名格式为com.sun.proxy.$ProxyN
  2. 方法转发:生成的代理类实现目标接口,并重写方法,将调用转发到InvocationHandler.invoke()
  3. 反射调用invoke()方法通过Method.invoke()反射调用目标方法,实现增强逻辑与业务逻辑的解耦。
局限性
  • 必须实现接口:JDK动态代理只能代理实现了接口的类。
  • 性能开销:反射调用比直接调用慢,适合代理逻辑复杂但调用频率低的场景。

1.4 CGLIB动态代理

实现原理

CGLIB(Code Generation Library)通过字节码生成技术动态创建目标类的子类,并重写非final方法实现代理。核心类为EnhancerMethodInterceptor接口。

  • Enhancer:CGLIB的核心类,用于创建代理对象。
  • MethodInterceptor:方法拦截器接口,需实现intercept()方法定义增强逻辑。
代码示例:CGLIB代理
// 1. 引入CGLIB依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>// 2. 目标类(无需实现接口)
public class UserService {public void createUser(String name) {System.out.println("创建用户: " + name);}
}// 3. 实现MethodInterceptor
public class LogMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("[CGLIB代理] 方法" + method.getName() + "调用开始");Object result = proxy.invokeSuper(obj, args); // 调用父类(目标类)方法System.out.println("[CGLIB代理] 方法" + method.getName() + "调用结束");return result;}
}// 4. 创建代理对象
public class Client {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class); // 设置父类(目标类)enhancer.setCallback(new LogMethodInterceptor()); // 设置拦截器UserService proxy = (UserService) enhancer.create(); // 创建代理对象proxy.createUser("张三");}
}
底层原理分析
  1. 子类生成:CGLIB使用ASM库生成目标类的子类,类名格式为目标类名$$EnhancerByCGLIB$$随机字符串
  2. 方法重写:子类重写目标类的非final方法,在方法中调用MethodInterceptor.intercept()
  3. 字节码操作:直接修改字节码,比JDK动态代理性能更高(避免反射调用)。
局限性
  • 无法代理final类/方法:由于基于继承,final类或方法无法被重写。
  • 安全性风险:字节码操作可能绕过权限检查,需谨慎使用。

1.5 三种代理方式对比

特性静态代理JDK动态代理CGLIB代理
实现方式手动编写代理类反射+接口ASM字节码+继承
代理目标接口或类仅接口类(非final)
性能高(直接调用)中(反射调用)高(字节码生成)
灵活性低(编译期确定)中(运行时生成)高(运行时生成)
典型应用简单增强场景Spring AOP(接口代理)Spring AOP(类代理)

二、策略模式(Strategy Pattern)

2.1 定义与应用场景

策略模式定义一系列算法,将每个算法封装为独立的策略类,并使它们可相互替换。核心作用是消除复杂的条件判断(if-else/switch),实现算法的动态切换。
在这里插入图片描述

典型应用场景

  • 支付方式选择(支付宝、微信、银行卡)
  • 排序算法切换(快速排序、冒泡排序)
  • 折扣策略(满减、打折、优惠券)

2.2 结构与实现

核心角色
  • 策略接口(Strategy):定义算法的公共接口。
  • 具体策略(Concrete Strategy):实现策略接口的具体算法。
  • 上下文(Context):持有策略对象的引用,负责调用策略。
代码示例:支付策略
// 1. 策略接口
public interface PaymentStrategy {void pay(double amount);
}// 2. 具体策略:支付宝支付
public class AlipayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("使用支付宝支付: " + amount + "元");}
}// 3. 具体策略:微信支付
public class WechatPayStrategy implements PaymentStrategy {@Overridepublic void pay(double amount) {System.out.println("使用微信支付: " + amount + "元");}
}// 4. 上下文类
public class PaymentContext {private PaymentStrategy strategy;// 构造器注入策略public PaymentContext(PaymentStrategy strategy) {this.strategy = strategy;}// 动态切换策略public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}// 执行支付public void executePayment(double amount) {strategy.pay(amount);}
}// 5. 客户端调用
public class Client {public static void main(String[] args) {PaymentContext context = new PaymentContext(new AlipayStrategy());context.executePayment(100); // 使用支付宝支付context.setStrategy(new WechatPayStrategy());context.executePayment(200); // 切换为微信支付}
}
输出结果:
使用支付宝支付: 100.0元
使用微信支付: 200.0元

2.3 优缺点

  • 优点

    • 符合开闭原则:新增策略无需修改原有代码。
    • 消除条件语句:用多态替代if-else判断。
    • 算法复用:策略类可在不同场景复用。
  • 缺点

    • 策略类数量增多:每个策略对应一个类。
    • 客户端需了解策略:客户端需知道所有策略并选择合适的策略。

三、模板方法模式(Template Method Pattern)

3.1 定义与应用场景

模板方法模式定义算法的骨架,将某些步骤延迟到子类实现。核心作用是复用公共流程,差异化实现细节,确保算法结构的稳定性。

典型应用场景

  • 框架设计(如Spring的JdbcTemplate)
  • 固定流程的业务逻辑(如报表生成、数据导入)
  • 生命周期管理(如Servlet的init-service-destroy)

3.2 结构与实现

核心角色
  • 抽象类(Abstract Class):定义模板方法(算法骨架)和基本方法(抽象方法、具体方法、钩子方法)。
  • 具体子类(Concrete Class):实现抽象方法,覆盖钩子方法(可选)。
代码示例:饮料制作流程
// 1. 抽象类(模板)
public abstract class Beverage {// 模板方法:定义算法骨架(final防止子类修改流程)public final void prepareRecipe() {boilWater();       // 公共步骤brew();            // 抽象步骤(子类实现)pourInCup();       // 公共步骤if (customerWantsCondiments()) { // 钩子方法控制流程addCondiments(); // 抽象步骤(子类实现)}}// 抽象方法:冲泡(子类实现)protected abstract void brew();// 抽象方法:加调料(子类实现)protected abstract void addCondiments();// 具体方法:烧水(公共步骤)private void boilWater() {System.out.println("烧水");}// 具体方法:倒入杯子(公共步骤)private void pourInCup() {System.out.println("倒入杯子");}// 钩子方法:是否加调料(默认加)protected boolean customerWantsCondiments() {return true;}
}// 2. 具体子类:咖啡
public class Coffee extends Beverage {@Overrideprotected void brew() {System.out.println("冲泡咖啡粉");}@Overrideprotected void addCondiments() {System.out.println("加糖和牛奶");}// 覆盖钩子方法:询问用户是否加调料@Overrideprotected boolean customerWantsCondiments() {return askUser(); // 模拟用户输入}private boolean askUser() {System.out.println("是否加糖和牛奶?(y/n)");return true; // 简化示例,默认返回true}
}// 3. 具体子类:茶
public class Tea extends Beverage {@Overrideprotected void brew() {System.out.println("浸泡茶叶");}@Overrideprotected void addCondiments() {System.out.println("加柠檬");}
}// 4. 客户端调用
public class Client {public static void main(String[] args) {Beverage coffee = new Coffee();coffee.prepareRecipe();// 输出:烧水 → 冲泡咖啡粉 → 倒入杯子 → 是否加糖和牛奶? → 加糖和牛奶Beverage tea = new Tea();tea.prepareRecipe();// 输出:烧水 → 浸泡茶叶 → 倒入杯子 → 加柠檬}
}

3.3 钩子方法的作用

钩子方法(Hook Method)是模板方法模式的关键扩展点,用于:

  • 控制流程:如示例中customerWantsCondiments()决定是否加调料。
  • 提供默认实现:子类可选择是否覆盖。
  • 扩展功能:在不修改模板方法的前提下增加新逻辑。

3.4 优缺点

  • 优点

    • 代码复用:公共流程在父类中实现,子类共享。
    • 强制流程:子类无法修改算法骨架,确保一致性。
    • 扩展性好:子类通过实现抽象方法扩展功能。
  • 缺点

    • 增加抽象类:系统复杂度提高。
    • 子类依赖父类:父类修改可能影响所有子类。

四、总结

三种模式的对比与选择

模式核心思想典型应用场景关键角色/类
代理模式控制对象访问,增强功能AOP、权限控制、延迟加载Proxy、InvocationHandler
策略模式封装算法,动态切换支付方式、排序算法、折扣策略Strategy接口、Context
模板方法固定流程,延迟步骤实现框架设计、固定流程业务逻辑抽象类(模板方法+钩子方法)

实践建议

  1. 代理模式:优先使用JDK动态代理(接口代理),无接口时选择CGLIB。
  2. 策略模式:当存在3个以上可替换算法时使用,结合工厂模式管理策略。
  3. 模板方法:流程固定但步骤实现可变时使用,通过钩子方法提供灵活性。

设计模式的核心价值在于解耦与复用,实际开发中需结合业务场景灵活选择,避免过度设计。

http://www.dtcms.com/a/278471.html

相关文章:

  • SpringMVC1
  • GraphRAG核心提示词工程完整中文版
  • VyOS起步指南:用Docker快速搭建网络实验环境
  • 分享三个python爬虫案例
  • HTML应用指南:利用GET请求获取河南省胖东来超市门店位置信息
  • STM32新建工程
  • HTB 赛季8靶场 - Outbound
  • 微算法科技技术创新,将量子图像LSQb算法与量子加密技术相结合,构建更加安全的量子信息隐藏和传输系统
  • 复习笔记 38
  • 安卓基于 FirebaseAuth 实现 google 登录
  • 【小米训练营】C++方向 实践项目 Android Player
  • C++ 左值右值、左值引用右值引用、integral_constant、integral_constant的元模板使用案例
  • 量子计算新突破!阿里“太章3.0”实现512量子比特模拟(2025中国量子算力巅峰)
  • ethers.js-5–和solidity的关系
  • RPC 框架学习笔记
  • Spark 之 like 表达式
  • 软件测试中的BUG等级与生命周期详解
  • 走近科学IT版:EasyTire设置了ip,但是一闪之后就变回到原来的dhcp获得的地址
  • ros2版本自定义插件的实现与热插拔
  • 设计模式(行为型)-迭代器模式
  • java 判断两个集合中没有重复元素
  • iOS高级开发工程师面试——Objective-C 语言特性
  • Linux(Ubuntu)硬盘使用情况解析(已房子举例)
  • rk3588ubuntu 系统移植AIC8800D Wi-Fi6/BT5.0芯片
  • EMQX + Amazon S3 Tables:从实时物联网数据到数据湖仓
  • C++函数指针
  • Redis作缓存时存在的问题及其解决方案
  • 云原生核心技术解析:Docker vs Kubernetes vs Docker Compose
  • Word 与 Excel 下拉菜单对比(附示例下载)
  • 前端将传回的List数据组织成树形数据并展示