用自定义版本替换Java类库中的类

javax / swing / plaf / basic中的 BasicLabelUI类受确认的bug影响。 在我的应用程序中,我需要 固定版本提供的function(为v9提交) 。 由于法律和技术原因,我仍然受限于受影响的JDK版本。

我的方法是在我的项目中创建一个包javax / swing / plaf / basic ,包含固定版本。

如何强制我的项目支持我在已安装的JDK中的缺陷类上包含的类的版本?

这必须具有一定的便携性,因为固定类也必须在客户端工作,并且必须忽略JDK安装中的缺陷类。 因此,我不想修改JDK,而是绕过这个特定的类。

正如其他答案所提到的,理论当然可以解压缩JVM的rt.jar文件并用兼容的bugfixed版本替换该文件。

任何类的Java类库(如Swing的类)都由引导类加载器加载该类加载器从此rt.jar中查找其类。 通常,不将类添加到此类路径,而不将其添加到此文件中。 有一个(非标准)VM选项

-Xbootclasspath/jarWithPatchedClass.jar:path 

您将在其中添加包含修补版本的jar文件,但这不一定适用于任何Java虚拟机。 此外, 部署更改此行为的应用程序非法的 ! 正如官方文件中所述 :

不要部署使用此选项的应用程序来覆盖rt.jar中的类,因为这违反了Java Runtime Environment二进制代码许可证。

但是,如果您将一个类附加到引导类加载器(通过使用检测API而不使用非标准API可能的话),运行时仍会加载原始类,因为引导类加载器在这种情况下首先搜索rt.jar 。 因此,如果不修改此文件,就不可能“破坏”破碎的类。

最后, 使用修补文件分发VM,即将其放入客户的生产系统中始终是非法的 。 许可协议明确规定您需要

[…]分发[Java运行时]完整且未经修改,仅作为applet和应用程序的一部分捆绑

因此,不建议更改您分发的VM,因为在发现此情况时可能会遇到法律后果。

当然,理论上您可以构建自己的OpenJDK版本,但是在分发时不能再调用二进制Java ,我认为您的客户不会通过您在答案中建议的内容来实现这一点。 根据经验,许多安全环境在执行之前计算二进制的哈希值,这将禁止两种调整执行VM的方法。

最简单的解决方案可能是创建您在启动时添加到VM进程的Java代理 。 最后,这与将库添加为类路径依赖项非常相似:

 java -javaagent:bugFixAgent.jar -jar myApp.jar 

Java代理能够在应用程序启动时替换类的二进制表示,因此可以更改错误方法的实现。

在您的情况下,代理看起来类似于以下内容,您需要将修补的类文件包含为ressource:

 public static class BugFixAgent { public static void premain(String args, Instrumentation inst) { inst.addClassFileTransformer(new ClassFileTransformer() { @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { if (className.equals("javax/swing/plaf/basic/BasicLabelUI")) { return patchedClassFile; // as found in the repository // Consider removing the transformer for future class loading } else { return null; // skips instrumentation for other classes } } }); } } 

javadoc java.lang.instrumentation包提供了有关如何构建和实现Java代理的详细说明。 使用此方法,您可以在不违反许可协议的情况下使用相关类的固定版本。

根据经验,Java代理是修复第三方库和Java类库中的临时错误的好方法,无需在代码中部署更改,甚至不需要为客户部署新版本。 事实上,这是使用Java代理的典型用例。

如何强制我的项目支持我在已安装的JDK中的缺陷类上包含的类的版本?

简单的答案 – 你不能。 至少,在严格遵守您应该使用受影响的Javs版本的约束时。

假设您可以在OpenJDK源代码库中识别合适的版本,则可以使用修补的bug构建自己的Java库。 但是,这不是真正的Java。 当然,它不会被限制为您受限制使用的“受影响的Java版本”。 (此外,您承诺将自己的补丁重新应用到当前版本的Java的每个新补丁版本中……)

理论上也可以将某些Java标准库类的修改版本放入JAR中,并使用-Xbootclasspath命令行选项将其添加到JVM的引导类路径中。 但这也等于改变了“受影响的Java版本”。

通过使用Java代理来使用类的修补版本来实现它也违反了规则。 它更复杂。


如果您和您的客户确实认为调整JVM是一个可接受的解决方案,那么通过bootstrap类路径执行此操作可能是最简单,最干净的方法。 它绝对是合法的1

但是,我建议您找到该bug的解决方法,直到发布带有您的修补程序的Java 9版本。


1 – 实际上,即使是来自修改源的构建方法也是合法的,因为Oracle二进制许可证不适用于此。 二进制许可证是关于分发Oracle二进制文件的修改版本。 另一个可能的问题是,如果您分发的版本与“真正的”Java不兼容,则可能违反了使用Java商标的条款,并调用您的发行版“Java”。 对此的解决方案是……不要称之为“Java”!

但是,不要只听从我的建议。 问律师。 更好的是,根本不要这样做。 这是不必要的复杂。