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).
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.
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:
- Make a retransforming program the "regular"
-javaagent
way, with premain
et al
- Rename
premain
to agentmain
- Create a temporary JAR file containing your agent classes and having a manifest pointing
Agent-Class
to your agent (agentmain
-containing) class, and Can-Retransform-Classes
set to true
- Obtain the PID of the target JVM (potentially the same process), and attach the temporary jar to it
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.
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!
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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…