13.设计模式-PROXY(代理)
一、模式定义与核心思想
代理模式是一种结构型设计模式,其核心目标是通过代理对象控制对原始对象的访问,实现职责分离与功能增强。该模式通过引入中间层(代理)实现以下核心价值:
- 访问控制:限制或保护对敏感对象的直接访问(如权限校验、防火墙代理)
- 功能扩展:在不修改原始对象的前提下增加逻辑(如日志记录、性能监控)
- 资源优化:延迟大资源加载(虚拟代理)、复用高成本对象(如数据库连接池)
- 远程交互:简化分布式系统中远程调用的复杂性(RPC框架、Web服务代理)
核心设计原则:
- 开闭原则:通过代理层扩展功能,避免修改原始对象
- 单一职责:代理对象专注于非业务逻辑(如安全、缓存),真实对象专注核心功能
二、模式组成与UML类图
核心角色
- Subject(抽象主题)
-
- 定义真实对象与代理的公共接口(如
UserService
接口)
- 定义真实对象与代理的公共接口(如
- RealSubject(真实主题)
-
- 实现核心业务逻辑的被代理对象(如
UserServiceImpl
类)
- 实现核心业务逻辑的被代理对象(如
- Proxy(代理)
-
- 持有真实对象的引用,控制访问并增强功能(如权限校验、日志记录)
UML类图
classDiagram
class Subject {
<<interface>>
+request()
}
class RealSubject {
+request()
}
class Proxy {
-realSubject: RealSubject
+request()
+preProcess()
+postProcess()
}
Subject <|.. RealSubject
Subject <|.. Proxy
Proxy --> RealSubject
协作流程:
- 客户端通过代理对象调用方法
- 代理执行预处理(如权限校验)
- 代理调用真实对象的核心方法
- 代理执行后处理(如日志记录)
三、代码实现示例
场景:实现数据库查询的缓存代理
1. 静态代理实现
// 抽象主题(数据库接口)
interface Database {
String query(String sql);
}
// 真实主题(MySQL实现)
class MySQLDatabase implements Database {
@Override
public String query(String sql) {
System.out.println("执行SQL查询: " + sql);
return "查询结果";
}
}
// 代理类(缓存增强)
class CacheProxy implements Database {
private Database realDB;
private Map<String, String> cache = new HashMap<>();
public CacheProxy(Database db) {
this.realDB = db;
}
@Override
public String query(String sql) {
// 缓存检查
if (cache.containsKey(sql)) {
System.out.println("【缓存命中】" + sql);
return cache.get(sql);
}
// 真实查询
String result = realDB.query(sql);
cache.put(sql, result);
return result;
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
Database db = new CacheProxy(new MySQLDatabase());
db.query("SELECT * FROM users"); // 首次查询
db.query("SELECT * FROM users"); // 命中缓存
}
}
2. JDK动态代理实现
// 动态代理处理器
class LoggingHandler implements InvocationHandler {
private Object target;
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【日志】调用方法: " + method.getName());
return method.invoke(target, args);
}
}
// 客户端动态生成代理
public class DynamicClient {
public static void main(String[] args) {
Database realDB = new MySQLDatabase();
Database proxy = (Database) Proxy.newProxyInstance(
realDB.getClass().getClassLoader(),
realDB.getClass().getInterfaces(),
new LoggingHandler(realDB)
);
proxy.query("SELECT * FROM orders");
}
}
四、工业级源码应用
- Spring AOP
- 通过JDK动态代理(接口)和CGLIB(类继承)实现事务管理、日志切面
@Transactional
public void updateUser(User user) {
// 方法调用会被代理增强事务逻辑
}
- MyBatis Mapper接口
-
- 动态生成Mapper接口的代理实现,将接口方法映射为SQL执行
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
- Java RMI(远程方法调用)
-
- 通过
RemoteObject
生成存根(Stub)代理,隐藏网络通信细节
- 通过
- Android Binder机制
-
- 跨进程通信时自动生成代理类,封装IPC调用细节
- Hibernate延迟加载
-
- 使用虚拟代理延迟初始化关联对象,避免N+1查询问题
五、模式对比与选型建议
维度 | 静态代理 | 动态代理 |
---|---|---|
实现方式 | 手动编写代理类 | 运行时反射生成代理类 |
灵活性 | 每新增方法需修改代理类 | 自动适配接口所有方法 |
适用场景 | 简单接口、少量方法 | 复杂系统、需要统一增强逻辑 |
性能开销 | 编译时绑定,执行效率高 | 反射调用存在性能损耗 |
最佳实践:
- 简单场景优先选择静态代理(如权限校验代理)
- 框架开发优先使用动态代理(如Spring AOP)
- 性能敏感场景考虑字节码增强技术(如ASM、Byte Buddy)
总结
代理模式通过间接访问的设计哲学,在Java生态中展现出强大的生命力。从Spring框架的AOP到MyBatis的Mapper代理,该模式成功解决了横切关注点分离与系统扩展性的核心难题。掌握代理模式的关键在于准确识别访问控制需求,并在静态代理的直观性与动态代理的灵活性之间找到平衡点,从而构建出安全、高效且易于维护的系统架构。