您可以使用 Javaassist 以及任何其他字节码工程库来完成它。神奇之处在于Java Attach API,它允许程序附加到正在运行的 JVM(并修改加载的类)。
它可以在com.sun.tools.attach
包中找到,顾名思义,它是特定于 Oracle JVM 的。尽管如此,JDK 工具喜欢jstack
并jmap
使用它来支持它们的“附加到运行的 JVM”特性,所以可以肯定地说它会继续存在。
Attach API 上的文档描述性很强,这篇Oracle 博客文章演示了在运行时附加代理。一般来说,它归结为:
- 与等人一起以“常规”
-javaagent
方式制作重新转换程序premain
- 重命名
premain
为agentmain
- 创建一个临时 JAR 文件,其中包含您的代理类并具有指向
Agent-Class
您的代理( -包含agentmain
)类的清单,并Can-Retransform-Classes
设置为true
- 获取目标JVM(可能是同一进程)的PID,并将临时jar附加到它
值得庆幸的是,API 无需您做太多工作就可以做到这一点,但如果您在运行时生成 JAR,打包代理所需的所有类可能会有点棘手。
我希望包含一个演示代理来演示在运行时附加分析器,但它最终太长而无法发布。尽管如此,我还是把它放在了Github repo中。
这种方法的一个警告是,它使您的程序依赖于tools.jar
JDK 附带的,而 JRE 中不存在。您可以通过tools.jar
随应用程序一起发布(或提取到应用程序中)来解决此问题,但您仍需要在应用程序中提供attach
Attach API 所需的本机库。我已经包含了我可以在上面链接的存储库中找到的所有平台的库,尽管您也可以自己获取它们。
根据您的用例,这可能是理想的,也可能不是理想的。但它确实有效!
这在问题中并不清楚,但是如果您希望做的是在运行时用您自己的类完全“热交换”一个类,您不需要使用任何字节码操作库。相反,您可以单独编译您的类(确保相同的包、类名等transform
)并在目标类上调用时简单地返回新类的字节。