在这里插入图片描述
在娱乐圈,你想请刘德华唱歌,你不能直接打他电话。
你必须先打给他的经纪人 (Agent)
经纪人负责谈价钱、签合同、安排场地。
只有当这些杂事都搞定后,经纪人才会把话筒递给刘德华:“华仔,该你上台唱了。”

在 Java 世界里,代理模式 就是这个“经纪人”。
它在 不修改原对象代码 的前提下,控制了对原对象的访问,并能**“夹带私货”**(比如加日志、加权限、加事务)。

这就是 Spring AOP 的核心灵魂。


🎭 一、技术分析:静态 vs 动态

代理模式在 Java 中经历了从“石器时代”到“工业时代”的进化。

1. 静态代理 (Static Proxy) —— “专属经纪人”

  • 原理: 你手动写一个 Agent 类,里面持有一个 Star 对象。
  • 代码:
class Agent implements Star {
    private Star realStar;

    public void sing() {
        System.out.println("经纪人:谈合同..."); // 前置增强
        realStar.sing();                     // 真实业务
        System.out.println("经纪人:收钱...");   // 后置增强
    }
}

  • 缺点: 累死人。刘德华有一个经纪人,张学友也得配一个。如果你有 100 个 Service 类,你就得写 100 个代理类。

2. 动态代理 (Dynamic Proxy) —— “万能经纪公司”

  • 原理: 不需要手写代理类。Java 在运行期间,利用 反射机制 自动生成一个代理对象。
  • 核心: Proxy.newProxyInstance()InvocationHandler
  • 优点: 一个 Handler 就能代理无数个类。不管你是刘德华还是张学友,只要想唱歌,我这个通用逻辑都能给你加上“谈合同”和“收钱”的步骤。

🛡️ 二、面试重灾区:JDK 动态代理 vs CGLIB

这是面试官最爱问的“大家来找茬”。Spring 会自动在两者之间切换。

1. JDK 动态代理 (官方正统)

  • 规则: 必须要实现接口 (Interface)
  • 原理: 它是利用反射,生成一个实现了同样接口的匿名类。
  • 局限: 如果刘德华没有“歌手证”(没实现接口),JDK 就代理不了他。

2. CGLIB 动态代理 (第三方黑科技)

  • 规则: 不需要接口,直接代理类。

  • 原理: 继承 (Inheritance)。它在内存中动态生成一个 LiuDeHua子类,重写父类的方法。

  • 这也是为什么 Spring 中被代理的方法不能是 final 的(因为 final 不能被继承)。

  • 场景: 如果你的 Service 没有接口,Spring Boot 2.x 默认会用 CGLIB。


🎬 三、故事场景:Spring AOP 的魔法

为了搞懂 AOP (面向切面编程),我们将 代理模式 比作 舞台演出

1. 原始代码 (没有 AOP)

public void saveUser() {
    // 1. 开启事务 (杂事)
    transaction.begin();
    
    // 2. 真正的业务 (唱歌)
    userDao.save();
    
    // 3. 提交事务 (杂事)
    transaction.commit();
}

  • 痛点: 业务代码(唱歌)和非业务代码(杂事)混在一起。如果你有 100 个方法,这几行事务代码要复制 100 遍。

2. 代理模式 (AOP)

Spring 创建了一个 代理对象 (Proxy)

  • 你的代码:
@Transactional
public void saveUser() {
    userDao.save(); // 只管唱歌
}

  • Spring 幕后生成的代理:
public void saveUserProxy() {
    try {
        transactionManager.begin(); // 前置通知 (Before Advice)

        target.saveUser();          // 回调你的目标方法

        transactionManager.commit();// 返回通知 (AfterReturning)
    } catch (Exception e) {
        transactionManager.rollback(); // 异常通知 (AfterThrowing)
    }
}

结论: 你以为你注入的是 UserService,其实 Spring 偷偷把它掉包成了 UserService$$EnhancerBySpringCGLIB


🚀 四、实战:什么时候用代理?

  1. 日志记录: 不要在每个方法里写 log.info("开始执行...")。写个 AOP,自动在所有 Controller 方法执行前打印日志。
  2. 权限控制: 不要在每个方法里检查 if (user.isAdmin())。写个代理,没权限的直接拦截。
  3. 延迟加载 (Lazy Loading):
  • MyBatis 查询 User 时,User 里面有个 List<Order>
  • 此时 MyBatis 返回的 List 其实是一个代理对象
  • 只有当你真正调用 list.get(0) 时,代理对象才会去数据库查询订单。这叫“虚代理”。

🎯 五、总结:无侵入式的增强

代理模式的灵魂在于 “无侵入”

它像一个透明的保护壳,包裹在真实对象外面。
外界看起来还是在调用原对象,但实际上,所有的请求都先经过了代理的过滤和加工。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐