跳到主要内容 Spring AOP 核心代理组件 MethodProxy 深度解析 | 极客日志
Java java
Spring AOP 核心代理组件 MethodProxy 深度解析 Spring AOP 基于 JDK 动态代理或 CGLIB 实现,其中 CGLIB 依赖 MethodProxy 组件进行方法拦截与调用。MethodProxy 封装了原始方法调用信息,通过 FastClass 机制实现高性能索引调用,避免传统反射开销。深入剖析 MethodProxy 源码结构、懒加载初始化流程及 invokeSuper 与 invoke 的区别,并结合 Spring AOP 执行链路说明其在环绕通知中的关键作用。
路由之心 发布于 2026/2/26 更新于 2026/4/18 1 浏览前言
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 包下。
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
package org.springframework.cglib.proxy;
public class MethodProxy {
}
这是一个公开的类,意味着它可以被框架内外所访问。它本身并不继承或实现任何特殊的接口。
核心字段解析
MethodProxy 内部通过几个关键字段来保存方法签名的信息以及用于快速调用的 FastClass 相关信息。
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 来创建的。
protected MethodProxy (Signature sig1, Signature sig2, CreateInfo createInfo) {
this .sig1 = sig1;
this .sig2 = sig2;
this .createInfo = createInfo;
}
核心静态工厂方法 create
这是创建 MethodProxy 实例的入口。Enhancer 在生成代理类的字节码时,会调用此方法来为每个被拦截的方法创建一个对应的 MethodProxy 对象。
public static MethodProxy create (Class c1, Class c2, String desc, String name1, String name2) {
Signature sig1 = new Signature (name1, desc);
Signature sig2 = new Signature (name2, desc);
CreateInfo createInfo = new CreateInfo (c1, c2);
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 第一次被用于调用方法时才进行初始化是明智的选择。
private void init () {
if (fastClassInfo == null ) {
synchronized (initLock) {
if (fastClassInfo == null ) {
CreateInfo ci = this .createInfo;
FastClass fc1 = FastClass.create(ci.c1);
FastClass fc2 = FastClass.create(ci.c2);
int i1 = fc1.getIndex(sig1);
int i2 = fc2.getIndex(sig2);
FastClassInfo fci = new FastClassInfo ();
fci.f1 = fc1;
fci.f2 = fc2;
fci.i1 = i1;
fci.i2 = i2;
this .fastClassInfo = fci;
this .createInfo = null ;
}
}
}
}
这个 init() 方法完美地展示了高性能、线程安全的懒加载模式。volatile 关键字确保了 fastClassInfo 在多线程间的可见性,并防止了 new FastClassInfo() 和赋值操作之间的指令重排,避免了其他线程拿到一个'半初始化'的对象。
核心实例方法 invoke 与 invokeSuper
这两个方法是 MethodProxy 对外提供功能的核心,它们是 MethodInterceptor 中最终调用的方法。
invokeSuper(Object obj, Object[] args)
这是在 Spring AOP 中最常用、也是唯一推荐使用的方法。它的作用是在代理对象上调用父类(即原始目标类)的同名方法。
public Object invokeSuper (Object obj, Object[] args) throws Throwable {
init();
FastClassInfo fci = this .fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
obj: 必须是 Enhancer 创建的代理对象实例。
args: 方法的参数数组。
执行逻辑:
调用 init() 确保 fastClassInfo 已被初始化。
从 fastClassInfo 中获取代理类的 FastClass 对象 (fci.f2)。
调用该 FastClass 对象的 invoke 方法,传入原始方法在代理类中的一个特殊索引 (fci.i2)、代理对象实例 obj 和参数 args。
这里的关键在于,CGLIB 在生成代理类时,不仅重写了目标方法,还为每个原始的父类方法生成了一个特殊命名(如 CGLIB$someMethod$0)的、可以直接调用 super.someMethod() 的新方法。invokeSuper 实际上就是通过 FastClass 机制,快速地定位并执行了这个特殊生成的方法,从而实现了对父类原始逻辑的调用。
invoke(Object obj, Object[] args)
这个方法的作用是在指定对象 obj 上调用与 MethodProxy 签名兼容的方法。这个 obj 不一定是代理对象本身,可以是任何一个拥有同样签名方法的目标对象。
public Object invoke (Object obj, Object[] args) throws Throwable {
init();
FastClassInfo fci = this .fastClassInfo;
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 动态生成一个新的类,继承自目标类。
MethodProxy 的实例化 :在生成上述代码时,Enhancer 会为每个被拦截的方法调用 MethodProxy.create(…),传入目标类、即将生成的代理类、方法描述符和方法名等信息,从而创建一个 MethodProxy 实例。这些实例通常作为代理类中的 static final 字段被持有,以避免重复创建。
生成 super 调用方法 :Enhancer 还会为每个原始方法 m 生成一个特殊的方法,例如 CGLIB$m$0,其方法体就是简单的 super.m(…)。MethodProxy 的 invokeSuper 最终调用的就是这个方法。
对于每一个被重写的方法 m,生成的代理方法体大致如下(伪代码):
@Override
public ReturnType m (ArgType1 arg1, ...) {
if (this .callback != null ) {
return (ReturnType) this .callback.intercept(this , Method_m_ref, args, MethodProxy_m);
} else {
return super .m(arg1, ...);
}
}
在这个过程中,MethodProxy 扮演了静态元信息的角色。它在类生成时被创建,并固化了调用原始方法所需的一切信息,等待着在运行时被 MethodInterceptor 使用。
MethodInterceptor:拦截逻辑的载体与 MethodProxy 的使用
当应用程序调用代理对象的某个方法时,执行权就交给了 MethodInterceptor.intercept()。
@Override
public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method execution..." );
Object result = proxy.invokeSuper(obj, args);
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 直接跳转到调用特定方法的代码块,避免了反射的查找和安全检查过程。
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 ]);
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 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 代理示例,这能更直观地体现其作用。
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());
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);
}
}
运行输出:
= = = > 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 索引调用等多种高级技术。