这是在8年前提出的here,从那时起已经过了8年。我想再次问这个问题,看看是否有人开发了一个猴子修补的框架,工具或库。
基本上我需要的是一个我应用自己的补丁的java应用程序。由于这个项目由另一个团队维护,我希望能够保留/应用我制作的任何补丁,以及他们制作的补丁。
答案 0 :(得分:10)
有许多技巧可能适用于此处,但您的问题过于模糊,无法将其缩小为单一答案。
"猴子补丁"从字面意义上说,它在Ruby中使用(即"在运行时替换类的方法",参见例如[1])是可能的" Java代理"并且"转换" API,但它比Ruby更难。
我需要将它应用于我自己的补丁
的java应用程序
如果有一个你拥有源代码的应用程序,比如git
,那么你可以分叉他们的项目,应用你自己的补丁并构建一个修改过的版本。
我希望能够继续将我制作的任何补丁应用到他们制作的补丁中。
如果您在分支上创建补丁,那么git
将很容易从"上游"中提取任何未来的更改。项目进入您的分支,并构建一个新的修改版本。
一种更接近Monkey Patching的简单技术是从目标应用程序编译单个类,并进行修改,并将其放在类路径上,而不是原始JAR。 (在这个答案:https://stackoverflow.com/a/381240/8261)
的旧猴子补丁中涵盖了这一点JVM按名称加载所有类,并将使用它在任何类的类路径中找到的第一个类文件,因此您可以从要修改的项目中逐个替换类。如果您拥有目标项目的源代码,则将其逐个文件复制到您的应用程序中,然后将修补程序应用于Java源代码。
(您需要使用此方法手动应用任何未来的上游更改。)
JVM有一个名为" Java Agents"的API。它允许您注册代码以在加载时修改类。
还有一个" retransform"允许您更改已加载类的定义的API。 JRebel使用它来更新正在运行的应用程序中的代码。 Ruby的补丁更加有限,你无法添加或删除方法(你可以改变方法体)。
这种机制由https://github.com/fommil/class-monkey用于"猴子补丁"例如,一个JVM错误。
答案 1 :(得分:3)
如果您创建自己的类加载器,几乎可以做任何事情。
This article about reloading classes at runtime并不完全正是您所做的事情,但这些信息对您想要做的事情非常有用。
this stackoverflow question/answer about changing the default class loader也会有所帮助。
通过使用MonkeyPatchClassLoader自定义类加载器加载类的不同版本,您可以让您的类版本执行不同的操作,或者他们可以将某些任务委派给类的原始版本(也就是说,您可以让该类的新版本封装该类的旧版本。
Java思考API也可能派上用场,具体取决于您想要更改的内容以及您希望如何更改它。
以下是关于类加载的第一个链接的示例代码段,只是为了让您思考这个方向:
// 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);
}
}
更改默认类加载器:
java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA
this is a good place to start with reflection。
如果这还不足以让你到达你想要的地方,那就用这些工具失败/难以克服的障碍来更新你的问题,也许我们可以克服它们。
答案 2 :(得分:2)
&#34;纯&#34;猴子修补需要一种动态语言,而Java则不然。从技术上讲,这是不可能的。进行运行时更改的最佳选择是进行字节码操作。用于此类工作的常用库是ASM。
答案 3 :(得分:2)
我想到了两个选项,尽管可能有更多的选择是“接近”,但并非如此:
如果您可以使用OSGi容器,那些可以动态加载,重新加载和卸载jar并基于此激活。描述OSGi值得单独讨论:参见What does OSGi solve?。
Netflix网关应用Zuul使用一种技术来轮询文件系统以获取过滤器代码并将其转换为使用运行时。它还将这些变化存储到Cassandra中。在内部,FilterLoader使用DynamicCodeCompiler实现GroovyCompiler。因此它实际上为任务编译Groovy文件,然后将它们加载到内存中。
对于实际的猴子补丁,反射api具有其所有限制,几乎就是你所拥有的。我觉得这是件好事。
答案 4 :(得分:0)
是的,你可以在运行时修补Java类。您可以使用sun.misc.Unsafe在运行时访问私有字段,然后运行类查找程序并更改引用以将它们设置为您想要的任何内容。在这里写了一篇博文。
https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/
我确实回复了原来的问题as well,所以我不确定你为什么没有提到它。它是ssl-config的一部分: