← 返回题目列表

什么是代理模式?它解决什么问题?

高频 简单 第 1 / 25 题 更新于 2026/07/28
代理模式结构型模式访问控制设计模式

简化版

代理模式是给目标对象提供一个代理对象,由代理对象控制对目标对象的访问,并在调用前后增加额外逻辑。它主要解决直接访问目标对象不方便、不安全、成本高,或者需要统一增强的问题。

详细版

代理模式的核心结构一般包括:

  • Subject:抽象主题,定义目标对象和代理对象共同的接口;
  • RealSubject:真实主题,真正执行业务逻辑;
  • Proxy:代理对象,持有真实对象引用,并在调用前后做增强。

典型代码:

interface UserService {
    void createUser(String name);
}

class UserServiceImpl implements UserService {
    public void createUser(String name) {
        System.out.println("创建用户:" + name);
    }
}

class UserServiceProxy implements UserService {
    private final UserService target;

    UserServiceProxy(UserService target) {
        this.target = target;
    }

    public void createUser(String name) {
        System.out.println("权限校验");
        target.createUser(name);
        System.out.println("记录日志");
    }
}

调用方使用代理:

UserService service = new UserServiceProxy(new UserServiceImpl());
service.createUser("Tom");

常见应用场景有:

  • 权限校验;
  • 日志记录;
  • 事务控制;
  • 缓存;
  • 延迟加载;
  • 远程调用;
  • 监控埋点;
  • Spring AOP。

面试时要强调:代理模式的重点不是“多包一层”,而是在不修改目标类的前提下控制访问、添加增强、隔离复杂逻辑。

完整版教学

一、为什么需要代理模式

假设业务中有一个用户服务:

userService.createUser("Tom");

一开始它只负责创建用户。后来需求变多了:

  • 创建前要鉴权;
  • 创建后要记录操作日志;
  • 方法耗时要上报监控;
  • 异常要统一转换;
  • 某些场景要走缓存;
  • 远程调用失败要重试。

如果把这些逻辑全部塞进 UserServiceImpl,业务类会越来越臃肿。它不再只关心“创建用户”,还要关心权限、日志、监控、异常、缓存。

代理模式的思路是:业务逻辑仍然放在真实对象里,访问控制和横切增强放在代理对象里。

这就是代理模式的第一层价值:让目标类专注业务,让代理类负责访问过程。

二、代理模式的核心思想

代理模式不是替代真实对象,而是“代替调用方去访问真实对象”。

调用链可以理解为:

客户端 -> 代理对象 -> 真实对象

客户端以为自己在调用 UserService,实际上调用的是代理对象。代理对象可以选择:

  • 是否允许访问;
  • 何时访问;
  • 调用前做什么;
  • 调用后做什么;
  • 异常时怎么处理;
  • 是否直接返回缓存结果;
  • 是否把请求转发到远程机器。

因此代理模式非常适合处理“访问过程”相关的问题。

三、代理模式和直接调用的区别

直接调用:

target.createUser("Tom");

代理调用:

proxy.createUser("Tom");

看起来只是多了一层,但设计意义完全不同。

直接调用时,客户端和真实对象强耦合。所有增强逻辑要么写在客户端,要么写在真实对象里。

代理调用时,客户端依赖的是统一接口,真实对象藏在代理后面。代理可以在不改客户端和真实对象主体逻辑的情况下扩展访问行为。

这也是 Spring AOP、MyBatis Mapper、RPC 框架大量使用代理思想的原因。

四、代理模式的常见类型

代理模式可以按目的分成很多类:

类型作用
静态代理手写代理类,结构清楚但类数量多
动态代理运行期生成代理对象,减少手写代理类
远程代理把本地方法调用转成远程网络调用
虚拟代理延迟创建成本高的对象
保护代理做权限控制
缓存代理避免重复访问目标对象
智能引用代理增加计数、监控、日志等

面试中最常考的是静态代理、JDK 动态代理、CGLIB 动态代理和 Spring AOP。

五、代理模式的边界

代理模式适合“访问控制和增强”,但不适合把业务流程都塞进代理。

如果代理对象里写了大量业务分支:

if (vip) {
    // 一套业务流程
} else {
    // 另一套业务流程
}

那可能不是代理模式要解决的问题,而是策略、模板方法、责任链等模式更合适。

代理应该增强访问过程,而不是取代真实对象成为新的业务大杂烩。

六、访问链是否真正可控

一次创建用户耗时 80 ms,鉴权 2 ms、日志 3 ms,代理后的同步延迟约为 85 ms;这 5 ms 是访问治理的显式成本。

Client -> Proxy(鉴权 2ms) -> RealSubject(80ms) -> Proxy(日志 3ms)

这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。

七、边界、代价与验证

检查维度应确认的内容
正确性把真实对象藏在代理后面,不等于可以忽略增强带来的延迟和失败语义。
适用边界代理应控制访问过程,若大量核心业务分支进入代理,应转向策略、模板方法或责任链。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。

易错点:把真实对象藏在代理后面,不等于可以忽略增强带来的延迟和失败语义。

八、常见误区与追问

  • 误区:代理对象会取代真实对象执行业务。 代理只决定是否、何时以及怎样转发,核心业务仍由 RealSubject 完成。
  • 误区:代理类存在,就代表所有调用都会经过代理。 只有客户端持有并调用代理引用时增强才生效;绕过代理直接调用目标对象,调用链自然不会出现增强。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:代理模式最稳定的判断标准是什么? 看它是否为真实对象提供替身并控制访问,而不是仅看是否出现包装结构。
  • 追问:代理模式和装饰器模式能只靠类图区分吗? 不能;两者都可能包装同一接口,必须结合“控制访问”还是“叠加职责”的设计意图区分。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

九、加强记忆

代理模式的记忆锚点是:客户端不直接碰真实对象,而是先经过代理。代理负责权限、日志、事务、缓存、远程调用等访问过程,真实对象继续专心做业务。