首页 > 解决方案 > 在没有代理的情况下检测 Java 字节码

问题描述

是否可以在没有 Java 代理的情况下动态检测 Java 字节码?我之前使用 Java 代理对字节码进行了检测,做了类似的事情:

ClassFileTransformer myTransformer = new Transformer();
instrument.addTransformer(myTransformer, true);
instrument.retransformClasses(classInstance);
instrument.removeTransformer(myTransformer);

但是,如果不使用 Java 代理,这可能吗?我想做的是调用一个方法,该方法将在 JVM 运行后的任何给定时间执行我的检测,而无需使用代理。

标签: bytecodeinstrumentationagentjava-bytecode-asm

解决方案


在没有实现实例的情况下执行字节码转换的唯一方法Instrumentation

  • 可以在调用之前更改字节的自定义类加载器defineClass(仅限于通过该加载器加载的类)

  • 甚至在加载类之前就使用修改后的字节进行调用MethodHandles.Lookup.defineClass,这适用于具有延迟加载的广泛 JVM,但仅限于您自己的模块或向您的模块打开的模块

这两种方法都不能改变已经加载的类。这需要Instrumentation引用,并且 JVM 发出此类引用的唯一地方是 Java 代理的初始化方法。因此,要使用它,Java 代理是不可避免的,即使它可能只是一个存储引用的存根,供您的应用程序代码使用。

请注意,从 Java 9 开始,Launcher-Agent-Classjar 文件的 manifest 属性可以指定 Java 代理的类在启动指定的类之前Main-Class启动。这样,您可以轻松地让您的代理与您的 JVM 中的应用程序代码协作,而无需任何额外的命令行选项。代理可以简单到agentmain在主类中有一个方法,将Instrumentation引用存储在静态变量中。

java.lang.instrument参阅软件包文档……</p>

Instrumentation当 JVM 还没有通过 Agents 启动时,获取实例会比较棘手。通常,它必须支持在启动后启动代理,例如通过 Attach API。这个答案在其结尾展示了这样一种自我连接来获得Instrumentation. 当您的应用程序 jar 文件中有必要的清单属性时,您甚至可以将其用作代理 jar 并省略临时存根文件的创建。

但是,最近的 JVM 禁止自连接,除非-Djdk.attach.allowAttachSelf=true在启动时指定,但我想,在启动时采取额外的步骤,正是你不想做的。避免这种情况的一种方法是使用另一个过程。所有这个过程所要做的就是附加到您的原始进程并告诉 JVM 启动代理。然后,它可能已经终止,其他一切的工作方式与引入此限制之前的方式相同。

本评论所述,Byte-Buddy 已经实现了这些必要的步骤,并且精简后的 Byte-Buddy-Agent 仅包含该逻辑,因此您可以使用它在其上构建自己的逻辑。


推荐阅读