使用特定的 class 加载程序从 ``byte[]`` 加载 class

load a class from ``byte[]`` using a specific class loader

假设我想动态创建 class B <: A 我需要在 A 可见的任何地方可见。

(严格来说,我想创建 class B <: A 作为现有 C <: A 的替代方案,并要求它在任何地方都可见,其中 C 是可见的。但这是一个细节。)

如果我正确理解 classloader 层次结构,那么从加载 A 的同一个 classloader 加载 B 应该可以解决问题。

ByteBuddy 具有指定 classloader 注入新 class

的功能
ByteBuddy byteBuddy = new ByteBuddy();

DynamicType.Builder builder = byteBuddy
        .subclass(A.class)
        .name("B")
        .andSoOn
        .blah
        ;

DynamicType.Unloaded<?> loadable = builder.make();


loadable.load(A.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);

我可以通过

得到生成的字节码
 byte[] bytecode = loadable.getBytes();

然后我可以修改并重新加载。

不过,我 运行 遇到了一些问题:

即使我能让它工作,我也不知道加载过程是否会重新执行静态初始化程序(我想要它以及我试图用另一个代码测试的内容)问题)与否。

所以 "safer" 路线是在加载 class 之前从 ASM 获取完成的字节码,然后直接加载 that 字节码。

我该怎么做呢?

"obvious" 方法是从 byte[] 中获取 DynamicType.Unloaded,然后像上面那样使用它的 load 方法。

不过我似乎找不到办法做到这一点。

ClassLoaderByteArrayInjector has a very promising name. But it expects a TypeDescriptor,我似乎无法卸载 class。

我该如何做到这一点?

请注意,对于任何 ClassLoadingStrategy,只有类型的名称很重要,因此您可以简单地使用 loadable.getTypeDescription(),例如

ClassLoadingStrategy.Default.INJECTION.load(A.class.getClassLoader(),
    Collections.singletonMap(loadable.getTypeDescription(), finalBytes));

只要你后面的改造没有改名字。如果您要使用生成的映射,您必须知道 TypeDescription 不会反映您在 loadable.getBytes() 之后所做的更改,因此您可以根据生成的 Class<?> 重新创建类型描述,例如通过 new TypeDescription.ForLoadedType(resultClass).

请注意,您链接到的 class 有一个 方法,它只采用名称和字节码,而不是 static,所以本文档承诺

Class<?> resultClass
    = new ClassLoaderByteArrayInjector(A.class.getClassLoader())
    .inject("B", finishedClass);

应该可以(虽然我无法测试)。

请注意,对于很多情况,例如A 从不引用 B 并且 B 不访问 A 的 package-private 元素,您可以简单地定义一个 class 和派生的 class 装载机

Class<?> resultClass = new ClassLoader(A.class.getClassLoader()) {
    Class<?> get(String name, byte[] code) {
        return defineClass(name, code, 0, code.length);
    }
}.get("B", finishedClass);

而不是将其注入 A 的加载程序。

请注意,ClassLoaderByteArrayInjector 来自 Byte Buddy 的早期测试版,现已不存在。您可能正在寻找 ClassInjector capabilities?

您可以使用这些注入器,但如果您首先使用 Byte Buddy 修改字节码,则可以只使用构建在 class 注入器之上的 class 加载策略进行注入策略。