问题
This was asked 8 years ago here and since then 8 years have passed. I wanted to ask this question again to see if anyone has developed a framework, tool or library that does monkey patching.
Basically what I need it for is a java application that I applied my own patch to. Since this project is maintained by another team I want to be able to keep/apply any patch I make, to the patches they make.
回答1:
There are a number of techniques that might be applicable here, but your question is too vague to narrow them down to a single answer.
"Monkey patching" in the literal sense that it is used in Ruby (i.e. "replace methods of a class at runtime", see e.g. [1]) is possible with "Java Agents" and the "retransform" API, but it's much harder than it is in Ruby.
Source code patching
I need it for a java application that I applied my own patch to
If there is an app for which you have the source code, say in git
, then you could fork their project, apply your own patch and build a modified version.
I want to be able to keep apply any patch I make, to the patches they make.
If you create your patch on a branch then it will be easy with git
to pull in any future changes from the "upstream" project into your branch, and build a new modified version.
Replacing classes at class-load time via classpath precedence
An easy technique that more closely approximates Monkey Patching is to compile a single class from the target app, with modifications, and put it earlier on your classpath than the original JAR. (This is covered in the old Monkey Patching q at this answer: https://stackoverflow.com/a/381240/8261 )
The JVM loads all classes by name, and will use the first classfile it finds on the classpath for any class, so you can replace the classes one-by-one from the project you wish to modify. If you have the source for the target project, then copy that into your app file-by-file and then apply your patches to the java source.
(You will need to manually apply any future upstream changes with this approach.)
Transforming classes at class-load time or "retransforming" method bodies at any time via JVM Agents
The JVM has an API called "Java Agents" which lets you register code to modify classes at the time they are loaded.
There is also a "retransform" API that lets you change the definition of already loaded classes. This is used by JRebel to update code in running applications. It's much more limited that Ruby's monkey patching in that you can't add or remove methods (you can change method bodies).
This mechanism is used by https://github.com/fommil/class-monkey to "monkey patch" a JVM bug, for example.
回答2:
You can do almost anything if you create your own class loader.
This article about reloading classes at runtime is not exactly what you are doing but the information can be very useful for what you want to do.
And this stackoverflow question/answer about changing the default class loader would also be helpful.
By loading a different version of the class with your MonkeyPatchClassLoader custom class loader, you can have your versions of the classes do something different, or or they can delegate certain tasks to the original version of the class (that is, you could have the new version of the class encapsulate the old version of the class).
Java reflections API might also come in handy, depending on what you want to change and how you want to change it.
Here is a sample code snippet from the first link about class loading just to get you thinking in that direction:
// Every two seconds, the old User class will be dumped, a new one will be loaded and its method hobby invoked.
public static void main(String[] args) {
for (;;) {
Class<?> userClass = new DynamicClassLoader("target/classes")
.load("qj.blog.classreloading.example2.ReloadingContinuously$User");
ReflectUtil.invokeStatic("hobby", userClass);
ThreadUtil.sleep(2000);
}
}
To change the default class loader:
java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA
And this is a good place to start with reflection.
If this is not enough to get you to where you want, update your question with the obstacles that these tools are failing/having-difficulty overcoming, and maybe we can overcome them.
回答3:
"Pure" monkey patching requires a dynamic language, which Java is not. So technically it's not possible. Your best bet for making runtime changes is to do bytecode manipulation. A common library used for this type of work is ASM.
回答4:
Two options came to my mind, though there's probably plenty more that are "close", though not exactly that:
if you can use OSGi container, those can load, reload and unload jars on the fly and activate based on that. Describing OSGi is worth of separate discussion: see What does OSGi solve?.
Netflix gateway applicance Zuul uses a technique where it polls file system for filter code and takes those into use runtime. It also stores these changes into Cassandra. Internally, there's FilterLoader using DynamicCodeCompiler implementation, GroovyCompiler. So it actually compiles Groovy files for the task and then loads them into memory.
For the actual monkey patching, reflection api with all its limits is pretty much all you have. Which I think is a good thing.
回答5:
Yes, you can monkey patch Java classes at runtime. You can use sun.misc.Unsafe to access private fields at runtime, then run through a class finder and and change the references to set them to whatever you want. Wrote a blog post here.
https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/
I did reply to the original question as well, so I'm not sure why you didn't mention it. It is part of ssl-config:
https://github.com/lightbend/ssl-config/blob/master/ssl-config-core/src/main/scala/com/typesafe/sslconfig/ssl/MonkeyPatcher.scala
来源:https://stackoverflow.com/questions/42139413/is-it-possible-to-do-monkey-patching-in-java-if-not-is-there-an-alternative