问题描述
我想在运行时替换某些方法的内容。
I would like to replace the content of some methods at runtime.
我知道我可以使用 javassist 但是它没有因为我想要增强的类已经被系统 classLoader 加载了。
I know I can use javassist for this but it does not work because the classes I would like to enhance are already loaded by the system classLoader.
我怎么做才能替换a的内容运行时的方法?我应该尝试卸载课程吗?我怎样才能做到这一点 ?我看到它是可能的,但我无法弄明白该怎么做。
How can I do, to replace the content of a method at runtime ? Should I try to unload the class ? How can I do that ? I saw it was possible but I could not figure out how to do it.
如果可能的话,我想避免使用外部库,我想我自己编码。
If possible, I would like to avoid using an external lib for this, I would like to code it my-self.
更多信息:
- 我想要增强的类包含在框架中(在一个jar文件)
- 我的代码实际上是这个框架的一个插件
- 我的插件运行的框架有自己的 classLoader ,但是这个 classLoader 不加载自己的类(它将它们委托给系统类加载器)
- 我正在使用的框架是 Play 。
More information:- The class I would like to enhance is contained in a framework (in a jar file)- My code is actually a plugin of this framework- The framework in which my plugin runs has its own classLoader, but this classLoader does not load its own classes (it delegates them to the system class loader)- The framework I'm using is Play.
感谢您的帮助!
推荐答案
您可以使用Javaassist执行此操作,以及任何其他字节码工程库。神奇之处在于 Java Attach API ,它允许程序附加到正在运行的JVM(并修改加载的类)。
You can do it with Javaassist, as well as any other bytecode engineering library out there. The magic lies in the Java Attach API, which allows programs to attach to running JVMs (and modify loaded classes).
可以找到它在包,顾名思义,特定于Oracle JVM。尽管如此,JDK工具如 jstack
和 jmap
使用它来支持它们附加到运行JVM功能,因此它是安全的说这是留下来。
It can be found in the com.sun.tools.attach
package, and as the name implies, is specific to the Oracle JVM. Nonetheless, JDK tools like jstack
and jmap
use it to support their "attach to running JVM" feature, so it's safe to say it's here to stay.
Attach API上的文档是相当具有描述性的,这是演示了在运行时附加代理。一般来说,它归结为:
The docs on the Attach API are fairly descriptive, and this Oracle blog post demonstrates attaching an agent at runtime. In general, it boils down to:
- 将重新转换程序设为常规
-javaagent
方式,premain
等 - 将
premain
重命名为agentmain
- 创建一个临时JAR文件,其中包含您的代理类并且清单指向
Agent-Class
到您的代理(agentmain
-containing)类,Can-Retransform-Classes
设置为true
- 获取目标JVM的PID(可能是相同的过程),并将临时jar附加到它上面
- Make a retransforming program the "regular"
-javaagent
way, withpremain
et al - Rename
premain
toagentmain
- Create a temporary JAR file containing your agent classes and having a manifest pointing
Agent-Class
to your agent (agentmain
-containing) class, andCan-Retransform-Classes
set totrue
- Obtain the PID of the target JVM (potentially the same process), and attach the temporary jar to it
值得庆幸的是,API可以在没有太多工作的情况下完成此任务,但如果您在运行时进行JAR生成,那么打包所有内容可能有点棘手您的代理所需的类。
Thankfully, the API can do this without much work on your part, though if you are doing the JAR generation at runtime it may be a bit tricky to package all the classes needed by your agent.
我希望包含一个演示代理,演示在运行时附加一个分析器,但它最终过于冗长而无法发布。尽管如此,我还是将它放在中。
I was hoping to include a demo agent demonstrating attaching a profiler at runtime, but it ended up being too lengthy to post. Nonetheless, I've put it up in a Github repo.
这种方法的一个警告是它使你的程序依赖于 tools.jar
那个与JDK一起发布,并且在JRE中不存在。您可以通过运送 tools.jar
与您的应用程序一起运送(或解压缩)来解决这个问题,但您仍然需要提供 attach
Attach API与您的应用程序所需的本机库。我已经在上面链接的存储库中找到了我可以找到的所有平台的库,不过你也可以自己获取它们。
A caveat of this approach is that it makes your program dependent on the tools.jar
that ships with JDKs, and which is not present in JREs. You can get around this by shipping tools.jar
with (or extracted in) your application, but you will still need to provide the attach
native library required by the Attach API with your application. I've included the libraries for all platforms I could find in the repository linked above, though you may obtain them by yourself too.
根据你的用例,这可能是可能不太理想。但它肯定有效!
Depending on your usecase, this may or may not be ideal. But it certainly works!
这个问题不明确,但如果你想做的事情完全是 hotswap运行时使用您自己的类,您不需要使用任何字节码操作库。相反,您可以单独编译您的类(确保相同的包,类名等),并在 transform
在目标类上调用。
This isn't clear in the question, but if what you wish to do is completely "hotswap" a class at runtime with your own, you do not need to use any bytecode manipulation library. Instead, you may compile your class separately (ensuring the same package, class name, etc.) and simply return the bytes for your new class when transform
is called on your target class.
这篇关于在运行时替换某些方法的内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!