如何将 class 注入 java.lang 包

How to inject a class into the java.lang package

在 OpenJDK 11 上尝试通过 java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch 注入位于 java.lang 命名空间中的 class 时,没有任何反应,也没有抛出任何错误。当放置 class 以注入到不同的包中时,它按预期工作。

JarFile jar = new JarFile(new File("file/to/bootstrap.jar));
instrumentation.appendToBootstrapClassLoaderSearch(jar);
// throws ClassNotFoundException java/lang/Dispatcher
Class.forName("java.lang.Dispatcher", false, null);
bootstrap.jar
 └─ java/lang/Dispatcher.class

我想这样做的原因是为了克服一些 OSGi 容器的问题。他们通常将对 bootstrap class 加载程序的委派限制为仅某些包。默认情况下,显然总是包含 java.* 这就是为什么我想把我的 Dispatcher class 放在那里。我知道 org.osgi.framework.bootdelegation 但 属性 只在初始化期间被读取。这意味着在运行时附加代理时,覆盖此值已经太迟了。

另一种方法是检测所有已知的 OSGi class 加载器并将代理 classes 列入白名单。但是对每个框架都这样做并针对每个版本进行测试似乎不太可行。

如何将 class 之类的自定义 java.lang.Dispatcher 注入到 bootstrap class 加载程序中?是否有其他模式或最佳实践来避免 OSGi 引导委托问题?

提供更多背景信息:

我的想法是只将这个Dispatcherclass注入bootstrapclass加载器。调度程序基本上只是持有一个静态地图。代理的其余 classes 将由专用的 URLClassLoader 加载,它是 bootstrap class 加载程序的子项。然后,代理将在调度程序的 Map 中注册 MethodHandles,以便注入的字节代码可以获取 MethodHandles,从而能够访问代理 class 加载到代理 class 加载程序中的 es。

可以通过使用不安全 API。自 Java 9 起,引导 class 加载程序的实现已更改为仅检查指定的 jmod 以查找已知包,但不再检查引导搜索路径。

Java 11 也删除了 sun.misc.Unsafe#defineClass 方法,但相同的方法在 jdk.internal.misc.Unsafe.

中仍然可用

您必须打开 class 的内部模块。您可以通过使用 sun.misc.Unsafe 来实现,它允许您在不进行可访问性检查的情况下编写字段值 (accessible),或者使用 Instrumentation 的官方 API.

如果您使用的是 Byte Buddy,请查看 ClassInjector 实现,它提供了所有方法的实现。

有一个 open ticket for adressing the need of Java agents to inject helper classes,但在解决之前,这是一个常见的解决方法。