是否可以在 Java 中进行猴子修补,如果不能,是否有替代方案?
Is it possible to do monkey patching in Java, if not is there an alternative?
这是 8 年前提出的问题 here,从那时起已经过去了 8 年。我想再问这个问题,看看有没有人开发过做猴子补丁的框架、工具或库。
基本上,我需要它的目的是 java 我应用了自己的补丁的应用程序。由于这个项目由另一个团队维护,我希望能够 keep/apply 我制作的任何补丁,到他们制作的补丁。
"Pure" 猴子补丁需要动态语言,Java 则不需要。所以技术上是不可能的。进行运行时更改的最佳选择是进行字节码操作。用于此类工作的常用库是 ASM.
我想到了两个选项,虽然可能还有很多 "close",但不完全是:
如果可以使用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 网关应用程序 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。所以它实际上为任务编译Groovy个文件,然后将它们加载到内存中。
对于实际的猴子补丁,反射 api 及其所有限制几乎就是您所拥有的。我认为这是一件好事。
这里可能适用多种技术,但您的问题过于模糊,无法将它们缩小为一个答案。
"Monkey patching" 在 Ruby 中使用的字面意义(即 "replace methods of a class at runtime",参见 [1])可能与 "Java Agents" 和"retransform" API,但比Ruby.
难多了
源代码补丁
I need it for a java application that I applied my own patch to
如果您有某个应用程序的源代码,例如 git
,那么您可以分叉他们的项目,应用您自己的补丁并构建修改后的版本。
I want to be able to keep apply any patch I make, to the patches they make.
如果您在分支上创建补丁,那么使用 git
可以很容易地将 "upstream" 项目中的任何未来更改引入您的分支,并构建一个新的修改版本。
通过class路径优先
在class-load时间替换classes
一种更接近 Monkey Patching 的简单技术是从目标应用程序编译单个 class,并进行修改,并将其放在 class 路径中比原始 JAR 更早的位置。 (这在旧的 Monkey Patching q 中包含在这个答案中:)
JVM 按名称加载所有 classes,并将使用它在 class 路径上为任何 class 找到的第一个 class 文件,因此您可以从您要修改的项目中替换 classes one-by-one。如果您有目标项目的源代码,请将其复制到您的应用程序 file-by-file,然后将您的补丁应用到 java 源代码。
(您将需要使用此方法手动应用任何未来的上游更改。)
在 class-load 时间或 "retransforming" 方法体随时通过 JVM 代理转换 classes
JVM 有一个名为“Java Agents”的 API,它允许您注册代码以在加载时修改 classes。
还有一个“retransform" API that lets you change the definition of already loaded classes. This is used by JRebel 来更新 运行 应用程序中的代码。它比 Ruby 的猴子补丁更受限制,因为您不能添加或删除方法(你可以改变方法体)。
此机制被 https://github.com/fommil/class-monkey 用于 "monkey patch" JVM 错误,例如。
如果您创建自己的 class 加载器,您几乎可以做任何事情。
This article about reloading classes at runtime 并不完全是您正在做的事情,但这些信息对于您想要做的事情可能非常有用。
this Whosebug question/answer about changing the default class loader 也会有帮助。
通过使用您的 MonkeyPatchClassLoader 自定义 class 加载程序加载不同版本的 class,您可以让您的 classes 版本做一些不同的事情,或者他们可以委托class原版本的某些任务(也就是说,你可以让新版本的class封装旧版本的class)。
Java 反射 API 也可能派上用场,具体取决于您要更改的内容和更改方式。
这是第一个 link 中关于 class 加载的示例代码片段,只是为了让您朝那个方向思考:
// 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);
}
}
要更改默认的 class 加载程序:
java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA
和this is a good place to start with reflection.
如果这还不足以让你到达你想要的地方,请用这些工具 failing/having-difficulty 克服的障碍更新你的问题,也许我们可以克服它们。
是的,您可以在 运行 时对 Java class 进行猴子补丁。您可以使用 sun.misc.Unsafe 在 运行 时访问私有字段,然后通过 class 查找器 运行 并更改引用以将它们设置为您想要的任何内容。在这里写了一篇博客post。
https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/
我确实回复了原问题as well,所以我不确定你为什么没有提到它。它是 ssl-config 的一部分:
这是 8 年前提出的问题 here,从那时起已经过去了 8 年。我想再问这个问题,看看有没有人开发过做猴子补丁的框架、工具或库。
基本上,我需要它的目的是 java 我应用了自己的补丁的应用程序。由于这个项目由另一个团队维护,我希望能够 keep/apply 我制作的任何补丁,到他们制作的补丁。
"Pure" 猴子补丁需要动态语言,Java 则不需要。所以技术上是不可能的。进行运行时更改的最佳选择是进行字节码操作。用于此类工作的常用库是 ASM.
我想到了两个选项,虽然可能还有很多 "close",但不完全是:
如果可以使用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 网关应用程序 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。所以它实际上为任务编译Groovy个文件,然后将它们加载到内存中。
对于实际的猴子补丁,反射 api 及其所有限制几乎就是您所拥有的。我认为这是一件好事。
这里可能适用多种技术,但您的问题过于模糊,无法将它们缩小为一个答案。
"Monkey patching" 在 Ruby 中使用的字面意义(即 "replace methods of a class at runtime",参见 [1])可能与 "Java Agents" 和"retransform" API,但比Ruby.
难多了源代码补丁
I need it for a java application that I applied my own patch to
如果您有某个应用程序的源代码,例如 git
,那么您可以分叉他们的项目,应用您自己的补丁并构建修改后的版本。
I want to be able to keep apply any patch I make, to the patches they make.
如果您在分支上创建补丁,那么使用 git
可以很容易地将 "upstream" 项目中的任何未来更改引入您的分支,并构建一个新的修改版本。
通过class路径优先
在class-load时间替换classes一种更接近 Monkey Patching 的简单技术是从目标应用程序编译单个 class,并进行修改,并将其放在 class 路径中比原始 JAR 更早的位置。 (这在旧的 Monkey Patching q 中包含在这个答案中:)
JVM 按名称加载所有 classes,并将使用它在 class 路径上为任何 class 找到的第一个 class 文件,因此您可以从您要修改的项目中替换 classes one-by-one。如果您有目标项目的源代码,请将其复制到您的应用程序 file-by-file,然后将您的补丁应用到 java 源代码。
(您将需要使用此方法手动应用任何未来的上游更改。)
在 class-load 时间或 "retransforming" 方法体随时通过 JVM 代理转换 classes
JVM 有一个名为“Java Agents”的 API,它允许您注册代码以在加载时修改 classes。
还有一个“retransform" API that lets you change the definition of already loaded classes. This is used by JRebel 来更新 运行 应用程序中的代码。它比 Ruby 的猴子补丁更受限制,因为您不能添加或删除方法(你可以改变方法体)。
此机制被 https://github.com/fommil/class-monkey 用于 "monkey patch" JVM 错误,例如。
如果您创建自己的 class 加载器,您几乎可以做任何事情。
This article about reloading classes at runtime 并不完全是您正在做的事情,但这些信息对于您想要做的事情可能非常有用。
this Whosebug question/answer about changing the default class loader 也会有帮助。
通过使用您的 MonkeyPatchClassLoader 自定义 class 加载程序加载不同版本的 class,您可以让您的 classes 版本做一些不同的事情,或者他们可以委托class原版本的某些任务(也就是说,你可以让新版本的class封装旧版本的class)。
Java 反射 API 也可能派上用场,具体取决于您要更改的内容和更改方式。
这是第一个 link 中关于 class 加载的示例代码片段,只是为了让您朝那个方向思考:
// 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);
}
}
要更改默认的 class 加载程序:
java -cp Example.jar -Djava.system.class.loader=example.ClassLoader example.ClassA
和this is a good place to start with reflection.
如果这还不足以让你到达你想要的地方,请用这些工具 failing/having-difficulty 克服的障碍更新你的问题,也许我们可以克服它们。
是的,您可以在 运行 时对 Java class 进行猴子补丁。您可以使用 sun.misc.Unsafe 在 运行 时访问私有字段,然后通过 class 查找器 运行 并更改引用以将它们设置为您想要的任何内容。在这里写了一篇博客post。
https://tersesystems.com/blog/2014/03/02/monkeypatching-java-classes/
我确实回复了原问题as well,所以我不确定你为什么没有提到它。它是 ssl-config 的一部分: