Instrument Java字节码,不带代理



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

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

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

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

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

  • 调用MethodHandles.Lookup.defineClass修改字节甚至在类已经加载,这工作在广泛的jvm与延迟加载,但仅限于您自己的模块或模块打开到您的模块

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

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

参见java.lang.instrument包文档…

在JVM没有使用Agents启动的情况下获取Instrumentation实例更加棘手。它必须支持在启动后启动代理,例如通过Attach API。这个答案在其结束时演示了这样的自我依恋,以获得Instrumentation的手。当您在应用程序jar文件中拥有必要的manifest属性时,您甚至可以将其用作代理jar,而不必创建临时存根文件。

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

正如在这条评论中提到的,Byte-Buddy已经实现了那些必要的步骤,精简的Byte-Buddy- agent只包含这些逻辑,因此您可以使用它在其之上构建自己的逻辑。

最新更新