使用特定的 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 加载策略进行注入策略。
假设我想动态创建 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 加载策略进行注入策略。