一、代理模式概述
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不改变原始类代码的情况下,通过引入代理类来给原始类添加额外的功能。
核心思想
控制访问:代理可以控制客户端对真实对象的访问
功能增强:在不修改原始对象的情况下,通过代理添加额外功能
延迟初始化:代理可以延迟创建开销大的对象,直到真正需要时
适用场景
远程代理:为远程对象提供本地代表
虚拟代理:创建开销大的对象时作占位
保护代理:控制对原始对象的访问权限
智能引用:在访问对象时执行额外操作(如引用计数、懒加载等)
二、代理模式结构
代理模式主要包含以下几个角色:
Subject(抽象主题):定义真实主题和代理主题的共同接口
RealSubject(真实主题):实现真正的业务逻辑
Proxy(代理):持有真实主题的引用,控制对真实主题的访问
三、Java实现案例
静态代理 - 数据库查询代理
首先
1. 定义抽象主题接口
public interface DatabaseQuery {
String query(String query);
}
2. 实现真实主题
public class RealDatabaseQuery implements DatabaseQuery {
@Override
public String query(String query) {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Result of: " + query;
}
}
3. 实现代理类
public class DatabaseQueryProxy implements DatabaseQuery {
private RealDatabaseQuery realQuery;
private Map<String, String> cache = new HashMap<>();
@Override
public String query(String query) {
// 缓存检查
if (cache.containsKey(query)) {
System.out.println("Returning cached result for: " + query);
return cache.get(query);
}
// 延迟初始化真实对象
if (realQuery == null) {
realQuery = new RealDatabaseQuery();
}
// 调用真实对象方法
String result = realQuery.query(query);
// 缓存结果
cache.put(query, result);
System.out.println("New query executed and cached: " + query);
return result;
}
}
测试类
public class ProxyTest {
public static void main(String[] args) {
DatabaseQuery proxy = new DatabaseQueryProxy();
// 第一次查询 - 执行真实查询
System.out.println(proxy.query("SELECT * FROM users"));
// 第二次相同查询 - 从缓存获取
System.out.println(proxy.query("SELECT * FROM users"));
// 新查询
System.out.println(proxy.query("SELECT * FROM orders"));
}
}
5. 输出结果
动态代理 - 方法调用日志记录
Java提供了内置的动态代理支持,可以动态创建代理类。
1. 定义业务接口
public interface UserService {
void addUser(String username);
void deleteUser(String username);
}
2. 实现真实业务类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
@Override
public void deleteUser(String username) {
System.out.println("Deleting user: " + username);
}
}
3. 实现InvocationHandler
public 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("Before method: " + method.getName());
if (args != null) {
System.out.println("Arguments: " + Arrays.toString(args));
}
// 调用真实方法
Object result = method.invoke(target, args);
// 方法调用后记录日志
System.out.println("After method: " + method.getName());
return result;
}
}
测试类
public class DynamicProxyTest {
public static void main(String[] args) {
// 创建真实对象
UserService realService = new UserServiceImpl();
// 创建动态代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
new LoggingHandler(realService)
);
// 通过代理调用方法
proxy.addUser("Alice");
proxy.deleteUser("Bob");
}
}
5. 输出结果
四、代理模式变体
1. 远程代理(Remote Proxy)
为不同地址空间的对象提供本地代表,如RMI(远程方法调用)中的stub就是远程代理。
2. 虚拟代理(Virtual Proxy)
延迟创建开销大的对象,如图片加载时先显示占位图。
3. 保护代理(Protection Proxy)
控制对原始对象的访问权限,如基于角色的访问控制。
4. 智能引用代理(Smart Reference Proxy)
在访问对象时执行额外操作,如:
引用计数
懒加载
对象锁定
五、代理模式优缺点
优点
职责清晰:真实角色只需关注业务逻辑,其他事务由代理处理
高扩展性:可以在不修改目标对象的情况下扩展功能
智能化:代理可以执行额外的智能操作
缺点
性能开销:代理模式会增加额外的处理,可能影响性能
复杂度增加:需要额外引入代理类,增加系统复杂度
静态代理类膨胀:如果接口增加方法,代理类和真实类都需要修改
六、实际应用场景
Spring AOP:基于动态代理实现面向切面编程
MyBatis:Mapper接口通过动态代理实现数据库操作
Hibernate:延迟加载使用代理实现
RPC框架:远程服务调用使用代理模式
安全框架:权限控制使用保护代理
七、与其他模式的关系
与装饰器模式:两者都基于组合,但目的不同。装饰器模式关注增强功能,代理模式关注控制访问
与适配器模式:适配器改变接口,代理实现相同接口
与外观模式:外观模式定义新接口,代理模式实现原接口
代理模式通过引入代理对象,可以在不修改原始类的情况下控制访问并添加额外功能。在实际开发中,合理使用代理模式可以提高系统的灵活性、安全性和可维护性。