初识动态代理

16 阅读2分钟

1. 什么是“代理”?(偷梁换柱)

在 Spring 容器里,你写了一个 EmpServiceImpl(大明星)。

但是,当你去 Controller 里注入 EmpService 的时候,Spring 并没有把那个真正的 EmpServiceImpl 给你,而是偷偷调包了。

它给你的是一个“假冒的替身”(代理对象)。

  • 你的视角:你看着这个替身长得跟大明星一模一样(因为它们实现了同一个接口),所以你以为你调用的就是大明星。
  • 实际情况:你调用的其实是替身。

2. 什么是“动态”?(现场打印名片)

  • 静态代理:为了搞个替身,你还得专门写一个 Agent.java 类,编译成 .class 文件。如果有 100 个明星,你就得写 100 个经纪人类,累死。
  • 动态代理牛就牛在这里! 程序运行的时候,JDK 或者 CGLIB(一个代码生成库)会在内存里,“刷”地一下,现场给你生成一个替身类的字节码。你不用写代码,这个替身是自动变出来的。

3. 动态代理是怎么干活的?(中间商赚差价)

当你在 Controller 里调用 empService.deleteEmp() 时,实际发生的流程是这样的:

  1. 拦截:你调用了替身(代理对象)的方法。
  2. 加料(前置通知) :替身先执行了 AOP 里定义的切面逻辑(比如:System.out.println("开始记录日志..."))。
  3. 甩锅(反射调用) :替身通过 Java 的反射机制(Reflection),去调用真正的大明星(目标对象)的 deleteEmp() 方法。
  4. 加料(后置通知) :大明星干完活,替身再执行剩下的切面逻辑。
  5. 交差:替身把大明星的返回结果,还给你。

4. 两种主流流派(JDK vs CGLIB)

Spring 底层会自动在以下两种方式中切换,面试常考:

第一派:JDK 动态代理(官方正版)

  • 条件:大明星必须有“工牌” (必须实现 Interface 接口)。
  • 原理:JDK 会在运行时生成一个类,这个类也戴着同样的“工牌”(实现了同样的接口)。
  • 局限:如果你的类没有实现接口,JDK 就傻眼了,干不了。

第二派:CGLIB 动态代理(民间高手)

  • 条件:大明星没有“工牌” (只是一个普通的类,没实现接口)。
  • 原理认干爹。CGLIB 会在运行时生成一个类,它是大明星的子类(继承了大明星)。
  • 局限:因为是继承,所以如果大明星被 final 修饰了(绝后了),CGLIB 就没法生成子类了。

总结

  • 代理:就是 Spring 给你发了个“高仿货”。
  • 动态:这个高仿货是程序运行通过字节码技术自动生成的,不用你手动写。
  • 作用:你调高仿货的方法,它帮你干完杂活(日志、事务)后,再去找真货干正事。