Skip to content

代理模式是 Spring AOP、MyBatis、RPC 框架的底层基石,但"静态代理、JDK 动态代理、CGLIB"这几个词平时总是一起出现又分不清。这篇把它从头捋一遍:代理解决什么、静态 vs 动态的区别、以及它和装饰器这对"长得一样"的兄弟怎么区分。

一、解决什么问题

代理模式解决的是**"在不改原类的情况下,控制对对象的访问"**。

典型诉求:

  • 延迟加载:对象创建很贵(大对象),先给个代理占位,真用到了才创建
  • 权限控制:访问前先校验权限
  • 远程调用:本地代理代表远程对象(RPC 就是这么做的)
  • 日志/监控:方法调用前后记录日志

核心思想:代理和目标实现同一个接口,客户端无感知地通过代理访问目标,代理在"转调"的前后插入控制逻辑。

二、静态代理

java
// ① 接口
public interface Subject {
    void request();
}

// ② 真实对象
public class RealSubject implements Subject {
    public void request() { System.out.println("真实业务逻辑"); }
}

// ③ 代理:实现同一接口,持有真实对象,前后加控制
public class Proxy implements Subject {
    private RealSubject realSubject;
    public void request() {
        System.out.println("前置:权限校验");   // 访问控制
        realSubject.request();                   // 转调真实对象
        System.out.println("后置:日志记录");   // 附加逻辑
    }
}

// 客户端:无感知,直接调代理
Subject subject = new Proxy();
subject.request();

缺点:每个真实对象都要手写一个代理类,接口方法一多,代理类又长又重复——所以有了动态代理。

三、动态代理

静态代理"每个类手写一个代理"太累,动态代理在运行时自动生成代理类,分两种实现:

  • JDK 动态代理:靠"实现接口"冒充,目标必须有接口
  • CGLIB:靠"继承生成子类"冒充,无接口也行,但 final 不行

两种实现的机制差异、完整代码、对比表,详见《Java 动态代理:JDK 代理 vs CGLIB》——那篇把"为什么有接口用 JDK、无接口用 CGLIB"讲透了。

代理模式结构:客户端无感知地通过代理访问真实对象,代理前后加控制

四、实际应用

  • Spring AOP:事务、日志、权限,底层就是动态代理——有接口用 JDK 代理,没接口用 CGLIB
  • MyBatis MapperUserMapper 没有实现类,就是 JDK 动态代理生成的(见《MyBatis 快速入门》)
  • RPC 框架:本地代理对象代表远程服务,调用时发起网络请求

五、代理 vs 装饰器(易混)

代码结构几乎一样,但意图不同

维度代理模式装饰器模式
目的控制访问(能不能用)增强功能(能做更多)
侧重点前置校验、延迟、远程层层叠加新能力
谁创建框架/容器自动生成使用方手动层层包装

一句话:代理是"门卫"(拦在前面管你能不能进),装饰器是"裁缝"(往对象身上加衣服)。装饰器的详解见《装饰器模式》。

小结

  • 代理解决"不改原类、控制访问",核心是代理与目标同接口 + 转调前后加控制
  • 静态代理要手写代理类,动态代理运行时生成
  • JDK 代理基于接口,CGLIB 基于继承(无接口时用)
  • 落地:Spring AOP、MyBatis Mapper、RPC
  • 与装饰器区别:代理"控制访问",装饰器"增强功能"

下一步可以看同为结构型的《外观模式》——它和代理相反,代理是"拦在单个对象前",外观是"把一堆复杂的对象封装成一个简单入口"(文章整理中)。