1. 概述
代理模式(Proxy Pattern)是一种结构型设计模式,它的核心思想是为某个对象提供一个代理对象,以控制对该对象的访问。这种模式常用于隐藏对象的真实实现、延迟初始化、权限控制、远程调用等场景。
代理对象通常会在调用真实对象之前或之后添加一些额外逻辑,例如权限验证、日志记录、缓存控制等,从而实现对原始对象的保护或增强。
2. 代理模式示例
我们以一个需要“延迟加载”的对象为例,比如一个数据库连接池或 SessionFactory。这些对象初始化成本较高,我们希望在真正使用时才进行初始化。
2.1 接口定义
public interface ExpensiveObject {
void process();
}
2.2 实现类
public class ExpensiveObjectImpl implements ExpensiveObject {
private static final Logger LOG = LoggerFactory.getLogger(ExpensiveObjectImpl.class);
public ExpensiveObjectImpl() {
heavyInitialConfiguration();
}
@Override
public void process() {
LOG.info("processing complete.");
}
private void heavyInitialConfiguration() {
LOG.info("Loading initial configuration...");
}
}
这个类在构造时会执行一个耗时的初始化操作。
2.3 代理类
public class ExpensiveObjectProxy implements ExpensiveObject {
private static ExpensiveObject object;
@Override
public void process() {
if (object == null) {
object = new ExpensiveObjectImpl();
}
object.process();
}
}
这个代理类实现了相同的接口,并在首次调用时才真正创建 ExpensiveObjectImpl
实例,实现懒加载(Lazy Initialization)。
2.4 使用示例
public static void main(String[] args) {
ExpensiveObject object = new ExpensiveObjectProxy();
object.process(); // 第一次调用,触发初始化
object.process(); // 第二次调用,复用已创建的对象
}
输出结果如下:
Loading initial configuration...
processing complete.
processing complete.
可以看到,初始化操作只在第一次调用时执行一次,后续调用直接复用对象。
3. 代理模式的应用场景
理解“如何使用”是基础,理解“何时使用”才是关键。代理模式适用于以下几种常见场景:
✅ 虚拟代理(Virtual Proxy)
当对象创建成本较高时,可以使用代理来实现延迟加载。只有在真正需要时才创建对象,节省系统资源。
✅ 远程代理(Remote Proxy)
当对象位于远程地址空间时,代理可以封装网络通信细节,让客户端像访问本地对象一样访问远程资源。
✅ 保护代理(Protection Proxy)
用于控制对对象的访问权限。例如,根据用户角色决定是否允许调用某个方法。
✅ 智能引用(Smart Reference)
在访问对象前后添加额外逻辑,如记录访问次数、缓存结果、日志记录等。
4. 代理模式的实现方式
Java 中实现代理主要有以下几种方式:
4.1 手动编码实现(静态代理)
如上面示例所示,通过手动编写代理类实现接口,控制对真实对象的访问。这种方式结构清晰,但不够灵活,每个接口都需要一个代理类。
4.2 JDK 动态代理(Dynamic Proxy)
JDK 提供了动态代理机制,可以在运行时动态生成代理类。适用于基于接口的代理。
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
使用方式:
ExpensiveObject real = new ExpensiveObjectImpl();
ExpensiveObject proxy = (ExpensiveObject) Proxy.newProxyInstance(
real.getClass().getClassLoader(),
new Class[]{ExpensiveObject.class},
new DynamicProxyHandler(real)
);
proxy.process();
4.3 CGLIB 动态代理
适用于没有接口的类。CGLIB 通过继承方式创建代理类,适用于更广泛的场景。
⚠️ 注意:CGLIB 不能代理 final 类和 final 方法。
5. 小结
代理模式是一个非常实用且常见的设计模式,在实际开发中被广泛用于以下场景:
- ✅ 延迟加载(懒加载)
- ✅ 权限控制
- ✅ 远程调用
- ✅ 日志记录、性能监控等AOP场景
在 Java 中,我们可以选择手动实现代理类,也可以使用 JDK 动态代理或 CGLIB 等框架来简化开发。合理使用代理模式,可以有效提升代码的可维护性和扩展性。
6. 示例代码地址
完整示例源码已上传至 GitHub:Proxy Pattern 示例代码(模拟地址,可根据实际项目替换)