代理模式:控制对象访问

FreeGuideOnline 最新 2026-06-18

代理模式:为对象访问装上“控制器”

代理模式(Proxy Pattern)是一种结构型设计模式,它让你能够提供一个代理对象来控制对另一个对象的访问。代理对象在客户端和目标对象之间充当中介,就像现实生活中的经纪人、防火墙或信用卡——它们本身不代表核心功能,却掌控着与核心功能交互的权限与流程。

本教程将带你从零理解代理模式,包括它的结构、多种变体、代码实现以及实际应用场景,帮助你掌握这一强大而优雅的设计思想。


什么是代理模式?

代理模式的核心思想是:为其他对象提供一种替身,以控制对这个对象的访问

想象你要去一家高级餐厅吃饭。你不能直接走进厨房让厨师为你做菜,而是需要通过服务员(代理)来下单。服务员负责检查菜单、传递需求、处理付款,甚至可能延迟上菜(比如等前菜吃完)。在这里,服务员就是厨房的代理,它控制着你对厨房资源的访问方式。

在软件设计中,代理模式同样如此。客户端并不直接调用真实业务对象,而是通过代理对象间接调用。代理对象可以在调用前后添加额外逻辑,如权限校验、延迟加载、日志记录等,而客户端对此几乎无感知。


代理模式的结构

代理模式通常包含三个角色:

角色 说明
Subject(抽象主题) 定义了 RealSubject 和 Proxy 的公共接口,这样客户端就可以无差别地使用代理或真实对象。
RealSubject(真实主题) 真正实现业务逻辑的类,是代理所要代表的实体。
Proxy(代理) 保存一个对 RealSubject 的引用,并实现与 Subject 相同的接口。它控制着对 RealSubject 的访问,并负责在必要时创建或删除它。
+-------------+        +------------------+
|   Client    | -----> |     Subject      | <interface>
+-------------+        +------------------+
                       | + request()      |
                       +------------------+
                              ^
                              |
                 +------------+------------+
                 |                         |
      +------------------+     +-----------------+
      |    RealSubject   |     |      Proxy      |
      +------------------+     +-----------------+
      | + request()      |     | - realSubject   |
      +------------------+     | + request()     |
                               +-----------------+

客户端依赖的是 Subject 接口,这意味着它无需关心自己拿到的是真实对象还是代理对象。代理持有真实对象的引用,并在自己的 request() 方法中调用真实对象的 request(),同时织入控制逻辑。


代理模式的四种常见类型

代理模式并非只有一种面孔。根据控制访问的目的不同,它可以演化为多种变体:

1. 远程代理(Remote Proxy)

为位于不同地址空间的对象提供本地代表。例如,当你调用一个远程服务器上的方法时,本地代理负责封装网络通信细节,让你感觉像是在调用本地对象。Java RMI、gRPC 等框架的 stub/skeleton 机制就是典型的远程代理。

2. 虚拟代理(Virtual Proxy)

延迟创建开销较大的对象,直到真正需要时才进行实例化。常见场景如图片查看器:在加载高分辨率大图之前,先用一个占位图标(代理)显示,待用户真正查看时再触发图片加载。

3. 保护代理(Protection Proxy)

控制对敏感对象的访问权限。代理可以检查调用者是否具备相应的权限,从而决定是否允许访问。例如,公司内部系统中的文件访问代理会根据员工角色决定其能否读取或修改某个文件。

4. 智能引用代理(Smart Reference Proxy)

在对象被访问时执行附加动作,如:记录对象被引用的次数、检查对象是否已被锁定、在对象被回收前释放资源等。它可以看作是一种“增值型”代理。


代理模式的代码实现

下面以一个 虚拟代理 为例,展示如何用 Java 实现图片延迟加载。

抽象主题:Image 接口

public interface Image {
    void display();
}

真实主题:高分辨率图片

public class HighResolutionImage implements Image {
    private String fileName;

    public HighResolutionImage(String fileName) {
        this.fileName = fileName;
        loadImageFromDisk();  // 模拟耗时加载
    }

    private void loadImageFromDisk() {
        System.out.println("Loading " + fileName + " from disk...");
        try {
            Thread.sleep(2000); // 模拟IO延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }
}

代理类:图片代理

public class ImageProxy implements Image {
    private String fileName;
    private HighResolutionImage realImage; // 延迟初始化

    public ImageProxy(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new HighResolutionImage(fileName); // 真正需要时才加载
        }
        realImage.display();
    }
}

客户端使用

public class Client {
    public static void main(String[] args) {
        // 使用代理,不会立即加载图片
        Image image1 = new ImageProxy("photo.jpg");
        Image image2 = new ImageProxy("cat.png");

        // 第一次调用 display 时才会真正加载
        image1.display(); 
        // 第二次调用不会再加载,直接显示
        image1.display();

        image2.display();
    }
}

输出结果
Loading photo.jpg from disk...
Displaying photo.jpg
Displaying photo.jpg
Loading cat.png from disk...
Displaying cat.png

这样一来,昂贵的加载操作被延迟到真正需要的时候,内存和性能都得到了优化。

动态代理(扩展知识)

除了手动编写代理类,Java 还提供了 java.lang.reflect.ProxyInvocationHandler 来实现动态代理。动态代理可以在运行时创建代理类,无需为每个真实主题单独编写代理类,这在 AOP(面向切面编程)框架中极为常见。


代理模式 vs. 装饰器模式

这两个模式结构非常相似,都采用组合和接口实现,但意图完全不同:

  • 代理模式:控制对对象的访问,强调对访问行为本身的管理(如延迟加载、权限控制)。
  • 装饰器模式:动态地给对象添加新功能,强调功能的增强,且装饰器可以多层嵌套。

一个简单的区分方法:代理通常由系统创建,代表真实对象完成“访问控制”;装饰器由客户端创建,透明地给对象“贴”上更多能力


代理模式的优缺点

优点

  • 职责清晰:真实对象专注于业务逻辑,代理负责非功能性的控制逻辑,符合单一职责原则。
  • 高扩展性:可以随时更换代理的实现方式,而不需要修改真实对象或客户端。
  • 智能化:通过代理可以在访问对象时加入更多智能操作,如缓存、延迟加载、日志等。
  • 安全性:保护代理可以有效防止未授权访问。

缺点

  • 代码复杂度增加:引入代理需要对每个需要控制的类创建一个代理类(静态代理时),增加了代码量。
  • 可能增加延迟:代理层本身增加了一次间接调用,对于性能极度敏感的场景需要权衡。
  • 设计难度:远程代理、智能引用代理等实现起来相对复杂,需要处理网络通信、并发等问题。

实际应用场景

  • 框架与中间件:Spring AOP 通过动态代理实现事务管理、日志拦截;MyBatis 使用代理为 Mapper 接口生成实现。
  • 安全系统:用保护代理控制用户对不同级别资源的访问。
  • 资源管理:文件系统代理可以限制对物理文件的直接操作,添加缓存和锁检查。
  • 延迟初始化:ORM 框架(如 Hibernate)常使用虚拟代理模式来延迟加载关联对象。
  • 远程服务:微服务架构中的服务调用(如 Feign)本质上是远程代理。

总结

代理模式通过引入一个中间层,在不改变原始对象的情况下,为对象访问赋予了精细化控制的能力。它既是解耦的利器,也是许多高级框架的基石。学习代理模式,你不仅能写出更安全、更高效的代码,还能深入理解 Spring、MyBatis 等框架背后的设计哲学。

记住三种角色、四种常见变体,并亲手实现一个虚拟代理或保护代理,你会发现:原来给对象装上“控制器”,可以如此优雅。

下一个教程中,我们将探讨与代理模式同样重要的 装饰器模式,敬请期待!