文章目录
前言
Spring AOP 的实现主要依赖于两种动态代理技术:JDK 动态代理和 CGLIB 代理 。当目标对象实现了一个或多个接口时,Spring 默认使用 JDK 动态代理;而当目标对象是一个没有实现任何接口的类时,Spring 则会采用 CGLIB 来创建其子类作为代理。在 CGLIB 代理机制中,MethodProxy 类是一个不可或缺的核心组件。每当通过代理对象调用一个方法时,这个调用都会被分发到一个 MethodInterceptor 接口的实现中,而 MethodProxy 对象正是作为参数传递给 intercept 方法的关键凭证 。它封装了调用原始方法(即父类方法)所需的一切信息,并借助一种名为 FastClass 的高效机制来执行这个调用,从而避免了传统 Java 反射带来的性能开销。
第一章:MethodProxy 在 Spring AOP 生态中的定位
要理解 MethodProxy,首先必须将其置于 Spring AOP 使用 CGLIB 实现代理的宏观语境中。CGLIB 代理的核心思想是:为一个目标类(Target Class)动态地生成一个子类(Proxy Class),并重写目标类中的非 final 方法。在这个子类中,对父类方法的调用被拦截,从而给了我们植入增强逻辑(Advice)的机会。
这个过程主要涉及以下四个核心角色,它们之间的关系构成了 CGLIB 代理的基础框架:
-
Enhancer (增强器)
- 角色:代理类的"工厂"或"建造者" 。
- 职责:Enhancer 是 CGLIB 库的入口点,负责配置和创建动态代理对象。开发者通过设置其父类(即被代理的目标类)、回调(Callback,通常是 MethodInterceptor 的实例)等参数,最后调用 enhancer.create() 方法。Enhancer 在底层会利用 ASM 字节码操作库,在内存中动态生成一个继承自目标类的代理类的 .class 文件内容,然后通过类加载器加载这个新生成的类,并实例化它。
-
MethodInterceptor (方法拦截器)
- 角色:AOP"通知"(Advice)逻辑的实际载体 。
- 职责:这是一个接口,类似于 JDK 动态代理中的 InvocationHandler。它只有一个核心方法 intercept(Object obj, Method method, Object[] args, MethodProxy proxy)。当代理对象的任何一个被重写的方法被调用时,该调用都会被 CGLIB 转发到 MethodInterceptor 的 intercept 方法中。所有前置、后置、环绕等增强逻辑都在这个方法内部实现。Spring AOP 中的 CglibAopProxy.DynamicAdvisedInterceptor 就是一个典型的例子。
-
Proxy Object (代理对象)
- 角色:应用程序面向的、经过功能增强的对象。
- 职责:它是 Enhancer 创建出来的目标类的子类实例。从外部调用者的角度看,它拥有与目标对象完全相同的行为接口。但其内部实现已经改变:每个方法的调用都会首先进入 MethodInterceptor 的处理逻辑。
-
MethodProxy (方法代理)
- 角色:目标方法的"快速调用句柄"。
- 职责:MethodProxy 对象是在 Enhancer 创建代理类的同时,为每一个被代理的方法所创建的。它封装了调用"原始方法"(即父类中未被重写的方法)所需的信息。在 MethodInterceptor 的 intercept 方法中,当我们希望执行原始业务逻辑时,我们不是通过 Java 反射(method.invoke())来调用,而是通过 methodProxy.invokeSuper(obj, args) 来调用 。这个 invokeSuper 方法是 CGLIB 高性能的核心所在,因为它最终会通过 FastClass 机制来执行调用。
四者协作流程简述:

从这个流程中可以清晰地看到 MethodProxy 的定位:它是在拦截器 MethodInterceptor 和原始目标方法之间的一个关键中介。它接收来自拦截器的调用请求,并以一种高效的方式将这个请求转发给父类(即原始目标对象)的相应方法。没有 MethodProxy,MethodInterceptor 就只能依赖于低效的反射来调用原始方法,CGLIB 的性能优势将荡然无存。
第二章:MethodProxy.java 源码深度剖析
Spring 框架内部捆绑了 CGLIB 的一个重新打包(repackaged)的版本,其包路径为 org.springframework.cglib。
类定义与签名
MethodProxy 类位于 org.springframework.cglib.proxy 包下 。
java
package org.springframework.cglib.proxy;
// ... import statements
public class MethodProxy {
// ... class body
}
这是一个公开的类,意味着它可以被框架内外所访问。它本身并不继承或实现任何特殊的接口。
核心字段解析
MethodProxy 内部通过几个关键字段来保存方法签名的信息以及用于快速调用的 FastClass 相关信息。
java
private Signature sig1;
private Signature sig2;
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
- private Signature sig1: Signature 是 CGLIB 内部用来表示方法签名(方法名 + 描述符)的类。sig1 通常存储了代理方法(子类中重写的方法)的签名。
- private Signature sig2: sig2 通常存储了原始方法(父类中的方法)的签名。这两个签名可能相同,但在某些高级场景下可能不同。
- private CreateInfo createInfo: 这是一个非常关键的内部类/对象,它持有了创建 FastClass 所需的上下文信息,例如目标类 (c1) 和代理类 (c2) 的 Class 对象。这个对象是在 MethodProxy 被 Enhancer 创建时就设定好的。
- private final Object initLock: 这是一个用于同步控制的对象,专门服务于 fastClassInfo 字段的懒加载初始化过程,以确保线程安全。
- private volatile FastClassInfo fastClassInfo: 这是实现高性能调用的核心。FastClassInfo 封装了两个 FastClass 对象(一个用于目标类,一个用于代理类)以及两个方法的索引(f1 和 f2 的索引)。该字段被声明为 volatile 并采用懒加载模式,这是为了确保在多线程环境下的可见性和防止指令重排序,配合 initLock 实现高效的线程安全的"双重检查锁定"(Double-Checked Locking)初始化 。
构造方法解析
MethodProxy 的构造方法通常是 protected 或 private 的,防止外部直接实例化。它的实例是由 Enhancer 在生成代理类的过程中,通过其静态工厂方法 create 来创建的。
java
// 伪代码
protected MethodProxy(Signature sig1, Signature sig2, CreateInfo createInfo) {
this.sig1 = sig1;
this.sig2 = sig2;
this.createInfo = createInfo;
}
核心静态工厂方法 create
这是创建 MethodProxy 实例的入口。Enhancer 在生成代理类的字节码时,会调用此方法来为每个被拦截的方法创建一个对应的 MethodProxy 对象。
java
// 伪代码
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
// 1. 根据方法描述符和方法名,创建两个 Signature 对象
Signature sig1 = new Signature(name1, desc);
Signature sig2 = new Signature(name2, desc);
// 2. 创建一个 CreateInfo 对象,封装了目标类和代理类的 Class 对象
CreateInfo createInfo = new CreateInfo(c1, c2);
// 3. 调用构造函数创建 MethodProxy 实例
return new MethodProxy(sig1, sig2, createInfo);
}
- c1: 目标类(父类)的 Class 对象。
- c2: 代理类(子类)的 Class 对象。
- desc: 方法的描述符,符合 JVM 规范,例如 (Ljava/lang/String;)V 表示一个接收 String 参数且无返回值的函数。
- name1: 代理方法的名字。
- name2: 原始方法的名字。
init 方法与懒加载机制
init 方法是 MethodProxy 内部设计的精髓之一,它实现了对 FastClass 这一"重资源"对象的懒加载和线程安全的初始化。FastClass 的生成过程涉及到字节码操作,相对耗时,因此只有在 MethodProxy 第一次被用于调用方法时才进行初始化是明智的选择。
java
// 伪代码
private void init() {
// 双重检查锁定 (Double-Checked Locking) 模式
if (fastClassInfo == null) {
synchronized (initLock) {
if (fastClassInfo == null) {
// 1. 从 createInfo 中获取目标类和代理类
CreateInfo ci = this.createInfo;
// 2. 为目标类和代理类分别创建 FastClass 对象
FastClass fc1 = FastClass.create(ci.c1);
FastClass fc2 = FastClass.create(ci.c2);
// 3. 在 FastClass 对象中根据方法签名查找方法的索引
int i1 = fc1.getIndex(sig1);
int i2 = fc2.getIndex(sig2);
// 4. 将 FastClass 对象和方法索引封装到 FastClassInfo 中
FastClassInfo fci = new FastClassInfo();
fci.f1 = fc1;
fci.f2 = fc2;
fci.i1 = i1;
fci.i2 = i2;
// 5. 将初始化好的 FastClassInfo 赋值给 volatile 字段
this.fastClassInfo = fci;
// 由于 createInfo 只在初始化时使用一次,之后可以置为 null 以便垃圾回收
this.createInfo = null;
}
}
}
}
这个 init() 方法完美地展示了高性能、线程安全的懒加载模式。volatile 关键字确保了 fastClassInfo 在多线程间的可见性,并防止了 new FastClassInfo() 和赋值操作之间的指令重排,避免了其他线程拿到一个"半初始化"的对象 。
核心实例方法 invoke 与 invokeSuper
这两个方法是 MethodProxy 对外提供功能的核心,它们是 MethodInterceptor 中最终调用的方法 。
- invokeSuper(Object obj, Object[] args)
这是在 Spring AOP 中最常用、也是唯一推荐使用的方法。它的作用是在代理对象上调用父类(即原始目标类)的同名方法。
java
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
init(); // 确保 FastClass 已初始化
FastClassInfo fci = this.fastClassInfo;
// 使用代理类的 FastClass (f2),调用其 invoke 方法,传入原始方法的索引 (i2)
return fci.f2.invoke(fci.i2, obj, args);
}
- obj: 必须是 Enhancer 创建的代理对象实例。
- args: 方法的参数数组。
- 执行逻辑:
- 调用 init() 确保 fastClassInfo 已被初始化。
- 从 fastClassInfo 中获取代理类的 FastClass 对象 (fci.f2)。
- 调用该 FastClass 对象的 invoke 方法,传入原始方法在代理类中的一个特殊索引 (fci.i2)、代理对象实例 obj 和参数 args。
这里的关键在于,CGLIB 在生成代理类时,不仅重写了目标方法,还为每个原始的父类方法生成了一个特殊命名(如 CGLIBsomeMethod0)的、可以直接调用 super.someMethod() 的新方法。invokeSuper 实际上就是通过 FastClass 机制,快速地定位并执行了这个特殊生成的方法,从而实现了对父类原始逻辑的调用。
- invoke(Object obj, Object[] args)
这个方法的作用是在指定对象 obj 上调用与 MethodProxy 签名兼容的方法。这个 obj 不一定是代理对象本身,可以是任何一个拥有同样签名方法的目标对象。
java
public Object invoke(Object obj, Object[] args) throws Throwable {
init(); // 确保 FastClass 已初始化
FastClassInfo fci = this.fastClassInfo;
// 使用目标类的 FastClass (f1),调用其 invoke 方法,传入原始方法的索引 (i1)
return fci.f1.invoke(fci.i1, obj, args);
}
- obj: 任意一个对象,通常是原始目标类的实例。
- args: 方法的参数数组。
- 执行逻辑:
- 调用 init()。
- 从 fastClassInfo 中获取目标类的 FastClass 对象 (fci.f1)。
- 调用该 FastClass 对象的 invoke 方法,传入原始方法在目标类中的索引 (fci.i1)、指定的对象实例 obj 和参数 args。
invoke 方法提供了更大的灵活性,允许将方法的调用委托给另一个不同的对象。但在标准的 Spring AOP 环绕通知中,如果错误地对代理对象本身使用 invoke (methodProxy.invoke(proxyObj, args)),会导致无限循环,因为这会再次触发代理对象的拦截器,造成堆栈溢出。因此,在AOP场景下,调用链的传递必须使用 invokeSuper。
第三章:MethodProxy 与 CGLIB 核心组件的交互机制
上一章我们解剖了 MethodProxy 的内部构造,现在我们将其放回 CGLIB 的动态世界中,看看它是如何与其他组件丝滑地协同工作的。
Enhancer:代理类的缔造者与 MethodProxy 的诞生
Enhancer 的 create() 过程是整个魔法的起点。这个过程可以概括为以下几个步骤:
- 确定代理策略:Enhancer 分析目标类,找出所有可被重写(非 final、非 static、非 private)的方法。
- 生成字节码 :
-
Enhancer 动态生成一个新的类,继承自目标类。
-
对于每一个被重写的方法 m,生成的代理方法体大致如下(伪代码):
java@Override public ReturnType m(ArgType1 arg1, ...) { // this.callback 是 Enhancer.setCallback() 设置的 MethodInterceptor if (this.callback != null) { // MethodProxy_m 是预先为方法 m 创建并静态存储的 MethodProxy 实例 return (ReturnType) this.callback.intercept(this, Method_m_ref, args, MethodProxy_m); } else { // 如果没有拦截器,直接调用父类方法 return super.m(arg1, ...); } } -
MethodProxy 的实例化:在生成上述代码时,Enhancer 会为每个被拦截的方法调用 MethodProxy.create(...),传入目标类、即将生成的代理类、方法描述符和方法名等信息,从而创建一个 MethodProxy 实例。这些实例通常作为代理类中的 static final 字段被持有,以避免重复创建。
-
生成 super 调用方法:Enhancer 还会为每个原始方法 m 生成一个特殊的方法,例如 CGLIBm0,其方法体就是简单的 super.m(...)。MethodProxy 的 invokeSuper 最终调用的就是这个方法。
-
在这个过程中,MethodProxy 扮演了静态元信息的角色。它在类生成时被创建,并固化了调用原始方法所需的一切信息,等待着在运行时被 MethodInterceptor 使用。
MethodInterceptor:拦截逻辑的载体与 MethodProxy 的使用
当应用程序调用代理对象的某个方法时,执行权就交给了 MethodInterceptor.intercept()。
java
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// obj: 代理对象本身
// method: 被拦截的 java.lang.reflect.Method 对象
// args: 方法参数
// proxy: Enhancer 为此方法创建的 MethodProxy 对象
// 1. 执行前置通知
System.out.println("Before method execution...");
// 2. 调用原始方法
// 这里的 proxy 就是 MethodProxy 实例,调用 invokeSuper
Object result = proxy.invokeSuper(obj, args); // 关键步骤
// 3. 执行后置通知
System.out.println("After method execution...");
return result;
}
MethodProxy 在此处的角色是执行引擎。MethodInterceptor 只需要简单地调用 proxy.invokeSuper(),无需关心底层的实现细节。MethodProxy 内部会完成 FastClass 的初始化(如果需要的话),并执行高效的、基于索引的方法调用。这种设计将"做什么"(拦截逻辑)与"怎么做"(高效调用原始方法)清晰地分离开来。
FastClass:高性能调用的基石
FastClass 是 CGLIB 性能超越反射的核心技术。让我们深入理解它的工作原理 。
对于一个给定的类 SomeClass,FastClass.create(SomeClass.class) 会动态生成一个新的类,例如 SomeClass$$FastClassByCGLIB ...。这个新生成的 FastClass 有两个关键方法:
- getIndex(Signature signature): 这个方法接收一个方法签名对象,然后返回该方法的一个整数索引。FastClass 在生成时会为目标类的所有方法建立一个从签名到整数索引的映射。
- invoke(int index, Object obj, Object[] args): 这个方法接收一个方法索引、目标对象和参数数组。其内部实现通常是一个巨大的 switch 语句(或类似的跳转结构),根据传入的 index 直接跳转到调用特定方法的代码块,避免了反射的查找和安全检查过程。
FastClass.invoke 伪代码示例:
java
public Object invoke(int index, Object obj, Object[] args) {
SomeClass target = (SomeClass) obj;
switch (index) {
case 0:
return target.methodA((String) args[[33]];
case 1:
target.methodB();
return null;
case 2:
return target.methodC((Integer) args[[34]], (Double) args[[35]];
// ... more cases
default:
throw new IllegalArgumentException("Cannot find method with index " + index);
}
}
这种基于整数索引的 switch 调用,其执行效率非常接近于直接的 Java 方法调用,远远快于 java.lang.reflect.Method.invoke()。
MethodProxy 正是 FastClass 机制的优雅封装者。MethodProxy.init() 方法负责创建 FastClass 并获取方法索引,而 invoke 和 invokeSuper 方法则负责调用 FastClass.invoke(),将开发者从直接操作 FastClass 的复杂性中解放出来。
第四章:MethodProxy 在 Spring AOP 中的执行流程与实战
现在,我们将所有碎片化的知识点整合起来,描绘一幅在 Spring Boot 应用中,一个被 AOP 拦截的方法调用的完整生命周期图景。
AOP 调用链路全景图
假设我们有一个 UserService 类,其 createUser 方法被一个日志切面所环绕。
-
启动阶段 (Bean Initialization):
- Spring IoC 容器启动,开始创建 UserService 的 bean。
- Spring 的 AOP 后置处理器(AnnotationAwareAspectJAutoProxyCreator)发现 UserService 符合切面 LoggingAspect 的切点表达式。
- 由于 UserService 是一个类,Spring AOP 决定使用 CGLIB 代理。CglibAopProxy 被创建。
- CglibAopProxy 内部使用 Enhancer 来创建一个 UserService 的子类,我们称之为 UserService$$ EnhancerBySpringCGLIB ...。
- Enhancer 设置回调为 CglibAopProxy.DynamicAdvisedInterceptor 的实例。这个拦截器持有了所有应施加于 UserService 的通知链(Advices)。
- 在生成代理类的过程中,Enhancer 为 createUser 方法创建了一个对应的 MethodProxy 实例,并将其静态存储在代理类中。
- 最终,IoC 容器中注册的 userService bean 实际上是这个代理对象的实例。
-
运行阶段 (Method Invocation):
- 应用程序中的其他组件(如 Controller)从容器中注入 UserService,并调用 userService.createUser(...)。
- 实际上,调用的是代理对象的 createUser 方法。
- 根据 CGLIB 生成的逻辑,代理对象的 createUser 方法会立即调用 DynamicAdvisedInterceptor.intercept() 方法,并将自身 (this)、createUser 的 Method 对象、参数以及为 createUser 预先创建的 MethodProxy 实例一并传入。
- 进入 Spring AOP 拦截器链: DynamicAdvisedInterceptor 开始工作。它会构建一个拦截器链(List< Object > chain),其中包含了例如 ExposeInvocationInterceptor 和我们自定义的 LoggingAspect 的环绕通知。
- 执行通知: 拦截器链开始执行。LoggingAspect 的环绕通知被调用。
- 在环绕通知内部,前置逻辑(如打印 "开始创建用户...")被执行。
- 当环绕通知需要执行原始方法时,它会调用 ProceedingJoinPoint.proceed()。
- proceed() 方法的调用在 Spring AOP 内部会沿着拦截器链继续传递,最终会到达链的末端,需要调用原始的目标方法。
- MethodProxy 登场: 链末端的逻辑最终会调用 CglibMethodInvocation.proceed(),这个方法内部会执行 this.methodProxy.invokeSuper(this.proxy, this.arguments) 。
- MethodProxy.invokeSuper 内部触发 init() 方法(如果是首次调用),创建 FastClass 并缓存索引。
- 然后,通过 FastClass 高效地调用了代理类中那个特殊生成的、直接调用 super.createUser() 的方法。
- 原始方法执行: UserService 中的原始 createUser 逻辑被执行。
- 返回与后置通知:
- createUser 的执行结果返回给 MethodProxy,再返回给 CglibMethodInvocation。
- 执行权沿着拦截器链反向回溯。
- LoggingAspect 的环绕通知接收到 proceed() 的返回值,执行后置逻辑(如打印 "用户创建成功...")。
- 最终结果通过 DynamicAdvisedInterceptor 返回给代理对象的 createUser 方法,再返回给最开始的调用者(Controller)。
代码示例:自定义拦截器中的 MethodProxy
虽然在 Spring AOP 中我们通常不直接操作 MethodInterceptor,但为了演示 MethodProxy 的用法,我们可以编写一个纯粹的 CGLIB 代理示例,这能更直观地体现其作用 。
java
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
class SampleService {
public String greet(String name) {
System.out.println("Executing original greet method...");
return "Hello, " + name;
}
}
// 自定义方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("===> Interceptor: Before calling " + method.getName());
// 核心:使用 MethodProxy 调用父类(原始)方法
// obj 是代理对象,args 是参数
Object result = proxy.invokeSuper(obj, args);
System.out.println("===> Interceptor: After calling " + method.getName() + ", result is: " + result);
// 可以在这里修改返回结果
return ((String) result).toUpperCase();
}
}
// 测试入口
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleService.class); // 设置目标类为父类
enhancer.setCallback(new MyMethodInterceptor()); // 设置回调
// 创建代理对象
SampleService proxyService = (SampleService) enhancer.create();
// 调用代理对象的方法
String finalResult = proxyService.greet("World");
System.out.println("\nFinal result from caller: " + finalResult);
}
}
运行输出:
bash
===> Interceptor: Before calling greet
Executing original greet method...
===> Interceptor: After calling greet, result is: Hello, World
Final result from caller: HELLO, WORLD
这个示例清晰地展示了:
- 对代理对象 proxyService.greet() 的调用被 MyMethodInterceptor.intercept() 捕获。
- 在 intercept 方法中,proxy.invokeSuper(obj, args) 成功地执行了 SampleService 中的原始 greet 方法逻辑。
- 拦截器可以在调用前后添加自定义逻辑,甚至改变最终的返回值。
结语
MethodProxy.java 是 Spring 框架(通过其内嵌的 CGLIB)实现高性能 AOP 代理的核心技术基石。它并非一个简单的反射包装器,而是一个设计精巧、高度优化的组件,其背后蕴含着字节码生成、懒加载、双重检查锁定以及 FastClass 索引调用等多种高级技术。