是否可以在没有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只包含这些逻辑,因此您可以使用它在其之上构建自己的逻辑。