# 什么是代理模式?一般用在什么场景?
8 min read
Table of Contents
什么是代理模式?一般用在什么场景?
代理模式面试回答
【核心定义】
代理模式是一种结构型设计模式,通过创建一个代理对象来控制对原始对象的访问,在不改变原始对象代码的前提下,为其提供额外的功能或访问控制。
【关键要点】
- 核心结构:包含三个角色 - 抽象主题(接口)、真实主题(具体实现类)、代理类(实现相同接口并持有真实主题的引用)
- 访问控制:代理对象作为客户端和真实对象之间的中介,可以控制对真实对象的访问时机和条件
- 功能增强:在不修改原始对象的前提下,代理可以在调用前后添加额外的逻辑(如日志、缓存、权限验证等)
- 透明性:客户端通常不知道自己在使用代理还是真实对象,因为它们实现了相同的接口
【深度推导/细节】
代理模式的实现方式
静态代理:
// 1. 定义接口interface Subject { void request();}
// 2. 真实主题class RealSubject implements Subject { public void request() { System.out.println("真实请求"); }}
// 3. 代理类class Proxy implements Subject { private RealSubject realSubject;
public void request() { if (realSubject == null) { realSubject = new RealSubject(); } preRequest(); // 前置增强 realSubject.request(); postRequest(); // 后置增强 }}动态代理(JDK):
- 基于接口实现,使用
Proxy.newProxyInstance()创建代理对象 - 通过
InvocationHandler接口统一处理所有方法调用 - 运行时动态生成代理类字节码
CGLIB代理:
- 基于继承实现,可以代理没有接口的类
- 通过
Enhancer创建子类代理 - 使用
MethodInterceptor拦截方法调用
性能与选择考量
- 静态代理:编译时确定,性能好但灵活性差,每个代理类只能代理一个接口
- JDK动态代理:需要接口,生成速度快,调用性能略低于静态代理
- CGLIB代理:无需接口,生成速度慢但调用性能好,final类/方法无法代理
【关联/对比】
代理模式 vs 装饰器模式
| 维度 | 代理模式 | 装饰器模式 |
|---|---|---|
| 目的 | 控制访问,增强非业务功能 | 增强对象功能,添加职责 |
| 关注点 | 访问控制、延迟加载、远程访问 | 功能扩展、组合替代继承 |
| 关系 | 代理和真实对象关系固定 | 装饰器和组件可以动态组合 |
| 创建时机 | 通常在编译时确定 | 运行时动态组合 |
与其他模式的关联
- 适配器模式:改变接口,代理模式保持接口一致
- 门面模式:简化复杂系统接口,代理模式控制单个对象访问
- 责任链模式:多个对象处理请求,代理模式只有一个代理对象
【典型应用场景】
1. 远程代理(Remote Proxy)
- 场景:分布式系统中,客户端调用远程服务
- 实现:代理对象处理网络通信细节,客户端像调用本地对象一样使用
- 示例:RPC框架、EJB、Spring Remoting
2. 虚拟代理(Virtual Proxy)
- 场景:创建开销大的对象需要延迟初始化
- 实现:代理先创建轻量级对象,真正需要时才创建真实对象
- 示例:大图片加载、复杂计算延迟执行
3. 保护代理(Protection Proxy)
- 场景:基于权限控制对对象的访问
- 实现:代理检查调用者权限,决定是否转发请求
- 示例:Spring Security的方法级安全、操作系统权限控制
4. 智能引用代理(Smart Reference)
- 场景:为对象访问添加额外管理功能
- 实现:在访问前后添加计数、缓存、日志等
- 示例:
- 引用计数:跟踪对象被引用次数
- 缓存代理:第一次访问后缓存结果
- 同步代理:多线程环境下的安全访问
- 日志代理:记录方法调用信息
5. Spring框架中的代理应用
- AOP实现:使用JDK动态代理(接口)或CGLIB(类)实现切面编程
- 事务管理:
@Transactional注解通过代理实现事务控制 - 安全控制:Spring Security使用代理进行权限验证
- 延迟加载:Hibernate和Spring Data的懒加载机制
6. 实际开发中的具体场景
- 权限验证:Web应用中的接口访问控制
- 日志记录:记录方法调用参数、执行时间、结果
- 性能监控:统计方法执行时间,监控系统性能
- 缓存加速:对耗时操作结果进行缓存
- 断路器模式:在微服务调用中添加熔断保护
- Mock测试:单元测试中模拟外部依赖
『面试官追问』
可能的问题:
-
JDK动态代理和CGLIB代理有什么区别?如何选择?
- JDK基于接口,CGLIB基于继承
- 性能对比:JDK生成快调用慢,CGLIB生成慢调用快
- Spring AOP默认策略:有接口用JDK,无接口用CGLIB
-
代理模式有什么缺点?
- 增加系统复杂度
- 可能带来性能损失(特别是动态代理)
- 调试困难(调用栈变深)
-
静态代理和动态代理各适合什么场景?
- 静态代理:代理关系固定、性能要求高、需要编译时检查
- 动态代理:代理关系多变、需要减少重复代码、框架开发
-
Spring AOP是如何实现代理的?
- 根据目标对象是否有接口选择代理方式
- 通过
ProxyFactoryBean或@AspectJ注解配置 - 使用
DefaultAopProxyFactory决定创建哪种代理
-
代理模式会导致哪些设计问题?
- 可能违反单一职责原则(代理类承担额外职责)
- 可能隐藏真实对象的复杂性
- 过度使用会导致系统难以理解和维护
回答技巧:
- 结合具体框架(Spring、MyBatis等)举例说明
- 提到实际项目中的应用经验
- 对比不同实现方式的优缺点
- 说明在微服务、分布式系统中的特殊应用
代理模式是实际开发中应用最广泛的设计模式之一,深入理解其原理和应用场景,能够帮助设计出更加灵活、可维护的系统架构。