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 示例代码(模拟地址,可根据实际项目替换)


原始标题:The Proxy Pattern in Java | Baeldung