代理模式(Proxy Pattern)是一种结构型设计模式,通过引入代理对象控制对目标对象的访问,在请求处理前后插入附加逻辑(如缓存、权限校验、延迟加载等),同时保持客户端与目标对象的解耦。以下是该模式的系统性解析:
一、核心定义与设计目标
-
定义
代理模式通过代理对象替代目标对象,提供与目标对象相同的接口,客户端通过代理间接访问目标,从而实现对目标对象的访问控制与功能增强。例如,明星经纪人作为代理控制艺人的工作安排。 -
设计目标
- 访问控制:限制或保护对敏感对象的直接访问(如数据库连接)。
- 功能增强:在不修改目标对象的前提下扩展功能(如日志记录、性能监控)。
- 延迟初始化:按需创建资源密集型对象(如大文件加载)。
二、模式结构与角色划分
-
核心角色
- 抽象主题(Subject):定义代理与目标对象的公共接口(如
Cache
接口)。 - 真实主题(Real Subject):实现核心业务逻辑的具体对象(如
JvmCache
)。 - 代理类(Proxy):持有目标对象的引用,实现附加逻辑并调用目标方法(如
RedisCacheDecorator
)。
- 抽象主题(Subject):定义代理与目标对象的公共接口(如
-
UML类图示例
+-------------------+ +---------------------------+
| Subject |<|-------|>| Proxy |
| +request() | | -realSubject: RealSubject |
+-------------------+ +---------------------------+
▲ ▲
| |
+-------------------+ +-------------------+
| RealSubject | | Client |
+-------------------+ +-------------------+
- 实现方式
- 静态代理:手动编写代理类,编译时生成(简单但类数量多)。
- 动态代理:运行时动态生成代理类,分为两类:
- JDK动态代理:基于接口,通过
InvocationHandler
实现。 - CGLib动态代理:基于继承,生成目标类的子类(无需接口)。
- JDK动态代理:基于接口,通过
三、优缺点分析
优点
- 解耦性:客户端仅依赖抽象接口,与目标对象实现解耦。
- 灵活性:动态扩展功能,符合开闭原则。
- 性能优化:通过延迟加载降低系统初始化开销。
缺点
- 类膨胀:静态代理需手动编写多个代理类。
- 性能损耗:动态代理的反射调用可能增加耗时。
- 复杂性:需明确代理逻辑的边界,避免过度设计。
四、适用场景
- 远程代理:
- 访问分布式服务(如RPC框架)。
- 虚拟代理:
- 延迟加载大资源(如图片、视频)。
- 保护代理:
- 权限校验(如数据库访问控制)。
- 智能引用:
- 缓存结果、日志记录、性能监控。
五、实现示例(Java)
以多级缓存系统为例,演示动态代理的应用:
// 抽象主题:缓存接口
interface Cache {
Object get(String key);
void put(String key, Object value);
}
// 真实主题:本地缓存
class LocalCache implements Cache {
private Map<String, Object> store = new HashMap<>();
@Override public Object get(String key) { return store.get(key); }
@Override public void put(String key, Object value) { store.put(key, value); }
}
// 动态代理增强类(JDK方式)
class CacheInvocationHandler implements InvocationHandler {
private Cache target;
public CacheInvocationHandler(Cache target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("get")) {
Object result = method.invoke(target, args);
if (result == null) {
result = fetchFromRemote((String) args[0]); // 模拟远程加载
target.put((String) args[0], result);
}
return result;
}
return method.invoke(target, args);
}
private Object fetchFromRemote(String key) { return "Remote Data: " + key; }
}
// 客户端调用
public class Client {
public static void main(String[] args) {
Cache proxy = (Cache) Proxy.newProxyInstance(
Cache.class.getClassLoader(),
new Class[]{Cache.class},
new CacheInvocationHandler(new LocalCache())
);
System.out.println(proxy.get("user_1001")); // 输出:Remote Data: user_1001
}
}
六、实际应用案例
- Java IO流:
BufferedInputStream
代理FileInputStream
实现缓冲功能。
- Spring AOP:
- 通过动态代理实现事务管理与日志切面。
- RPC框架:
- 客户端代理远程服务接口,屏蔽网络通信细节。
七、与其他模式的对比
模式 | 核心差异 |
---|---|
适配器模式 | 解决接口不兼容问题,而非功能扩展 |
装饰器模式 | 侧重叠加功能,代理侧重访问控制 |
外观模式 | 封装子系统简化接口,代理控制单个对象 |
八、总结
代理模式通过中介隔离与逻辑增强,为对象访问提供了灵活可控的解决方案。其核心价值在于平衡功能扩展与代码维护性,尤其适用于需要动态控制访问或延迟加载资源的场景。开发中需根据需求选择代理类型(静态/动态),避免过度设计导致系统复杂度失控。