如何通过 JVM 的 invokedynamic 指令实现非侵入式的 AOP 动态代理并减少运行时的类生成

小丽大大_8541

小丽大大_8541

2026-04-15

650人浏览

原创

不能靠 invokedynamic 直接实现非侵入式 AOP 动态代理,因其仅解决调用目标定位,不提供方法拦截钩子;真正需结合字节码增强织入 + 自定义 BSM 绑定 MethodHandle 实现低开销 AOP。

如何通过 jvm 的 invokedynamic 指令实现非侵入式的 aop 动态代理并减少运行时的类生成

不能靠 invokedynamic 直接实现“非侵入式 AOP 动态代理”——它本身不生成代理类,也不拦截方法调用;但它是构建低开销、无类膨胀 AOP 机制的关键基础设施。真正起作用的是:用 invokedynamic 替代反射调用 + 自定义引导方法绑定 MethodHandle + 配合运行时字节码增强(如 Byte Buddy)做切面织入。

为什么不能直接用 invokedynamic 做 AOP 代理

invokedynamic 指令本身不修改目标方法字节码,也不提供方法入口/出口钩子。它只解决“调用目标在哪”这个问题,而不是“调用前/后要做什么”。AOP 需要的是对原方法执行流的拦截与增强,这必须发生在目标方法被调用的前后位置,而 JVM 原生不支持这种钩子。

常见误解是:既然 Lambda 用 invokedynamic 实现,那我也能用它“动态替换方法调用”,从而绕过代理类。但实际中:

  • invokedynamic 的引导方法(BSM)只在首次调用时执行一次,后续完全跳过逻辑判断,无法插入切面代码;
  • 它返回的 CallSite 绑定的是一个固定 MethodHandle,不能在每次调用时动态决定是否执行前置/后置逻辑;
  • 若强行在 BSM 中做切面逻辑,会导致所有使用该 invokedynamic 的调用点共享同一份增强行为,丧失按方法、按注解、按条件定制的能力。

invokedynamic 如何真正降低 AOP 的类生成开销

传统基于 java.lang.reflect.Proxy 或 CGLIB 的代理,每次创建代理实例都可能触发新类生成(尤其是 CGLIB 子类),导致元空间压力大、类加载慢、热更困难。而 invokedynamic 可以把“调用转发”这个动作本身做得极轻量,前提是:把切面逻辑下沉到字节码增强层,让 invokedynamic 只负责最终执行。

典型协作路径是:

  • 用 Byte Buddy 或 ASM 在类加载时(或 redefine 时)修改目标方法字节码,在关键位置插入 invokedynamic 指令;
  • 该指令指向一个自定义引导方法,该方法根据当前对象、方法签名等上下文,动态查找并绑定一个已预热好的 MethodHandle(比如封装了日志、权限校验逻辑的静态方法);
  • 后续调用不再走反射或虚方法分派,而是直通 MethodHandle.invokeExact(),性能接近 invokevirtual;
  • 所有切面逻辑复用已有类中的静态方法,无需为每个代理对象生成新类。

例如:Spring AOP 5.3+ 已在部分场景尝试将 ReflectiveMethodInvocation 中的反射调用路径替换为 MethodHandle + invokedynamic 缓存,实测在高频调用下减少 15%~20% 的调用开销,且完全避免新增代理类。

Softr Studio
Softr Studio

Softr Studio是一款用于从 Airtable、Google Sheets 等数据源构建业务应用的无代码平台。

下载

自定义引导方法(BSM)的关键约束

你写的 BSM 不是随便一个 static 方法就能当引导方法——JVM 对签名、权限、返回值有硬性要求:

  • 必须是 static 方法,且参数顺序固定为:MethodHandles.Lookup lookup, String name, MethodType type;
  • 返回值必须是 CallSite 子类(常用 ConstantCallSite 或 MutableCallSite);
  • 方法必须声明在启动类路径(boot classpath)或由 BootstrapMethod 注册的类中,否则 lookup.findStatic() 会抛 IllegalAccessException;
  • BSM 内部若需访问目标类私有成员,必须通过 lookup 调用 findSpecial/findVirtual,不能直接 new 或反射;
  • 不要在 BSM 中做耗时操作(如 IO、锁竞争),它只执行一次,但会阻塞首次调用线程。

一个最小可用 BSM 示例:

public static CallSite bootstrap(MethodHandles.Lookup lookup,
                                 String name,
                                 MethodType type) throws Throwable {
    // 假设切面逻辑已封装在 MyAspect.logAndInvoke()
    MethodHandle target = lookup.findStatic(
        MyAspect.class, "logAndInvoke",
        MethodType.methodType(Object.class, Object.class, Object[].class)
    );
    return new ConstantCallSite(target);
}

invokedynamic 不解决但必须配合的问题

即使用了 invokedynamic,AOP 的核心难题仍需其他机制兜底:

  • 切点表达式(如 @annotation(Trace))解析仍需 AspectJ 或 Spring AOP 的匹配引擎,invokedynamic 不参与这部分;
  • 环绕通知(ProceedingJoinPoint)需要保存原方法调用上下文,这依赖字节码增强时插入的局部变量或栈帧操作,不是 invokedynamic 能提供的;
  • 如果要支持运行时启用/禁用切面,得用 MutableCallSite 并配合 volatile 字段控制 setTarget(),但频繁切换会破坏 JIT 优化,慎用;
  • 调试体验变差:IDE 无法跳转到被 invokedynamic 调用的真实切面方法,堆栈里只显示 MethodHandle.linkToStatic。

真正落地时,invokedynamic 是“最后一公里”的加速器,不是整套 AOP 的替代方案。最容易被忽略的一点是:它要求你放弃“运行时动态注册任意方法为切面”的灵活性,转而接受“编译/加载期静态绑定 + 运行期高速复用”的范式——这对工程可维护性提出了更高要求。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

动态代理

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

11657

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

8762

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

7532

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1244

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

1008

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1496

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

3049

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20311

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1335

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习