我们想要动态创建我需要在B <: A
可见的任何地方都可见的课程A
。
(严格来说,我想创建一个类B <: A
作为现有C <: A
的替代方案,并要求它在任何地方都可见,C
可见。但是那个&#39; sa细节。)
如果我正确理解了类加载器层次结构,那么从加载B
的同一个类加载器中加载A
应该可以解决问题。
ByteBuddy具有指定在哪个类加载器中注入新类
的功能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();
然后我可以修改并重新加载。
我遇到了一些问题,但是:Why doesn't ASM call my ``visitCode``?
即使我可以让它工作,我也不知道该加载过程是否会重新执行静态初始化器(我想要它以及我试图用另一个中的代码测试的内容)问题)与否。
所以&#34;更安全&#34; route是在加载类之前从ASM获取完成的字节码,然后直接加载那个字节码。
但我怎么做呢?
&#34;显而易见&#34;方法是从byte[]
获取DynamicType.Unloaded,然后使用上面的load
方法。
ClassLoaderByteArrayInjector有一个非常有前途的名字。但它期望TypeDescriptor,再次 - 我似乎无法获得卸载的课程。
如何解决此问题?
答案 0 :(得分:1)
请注意,对于任何ClassLoadingStrategy
,只有类型的名称很重要,因此您只需使用loadable.getTypeDescription()
,例如
ClassLoadingStrategy.Default.INJECTION.load(A.class.getClassLoader(),
Collections.singletonMap(loadable.getTypeDescription(), finalBytes));
只要您的后续转换没有更改名称。如果您要使用生成的地图,则必须注意TypeDescription
不会反映您在loadable.getBytes()
之后所做的更改,因此您可以从生成的Class<?>
中重新创建类型说明,例如通过new TypeDescription.ForLoadedType(resultClass)
。
请注意,您已链接到的类, 只采用名称和字节代码的方法,它只是static
,所以本文档承诺
Class<?> resultClass
= new ClassLoaderByteArrayInjector(A.class.getClassLoader())
.inject("B", finishedClass);
应该有效(我无法测试)。
请注意,对于很多情况,例如A
从不引用B
而B
不访问A
的包私有元素,您只需使用派生类加载器定义类
Class<?> resultClass = new ClassLoader(A.class.getClassLoader()) {
Class<?> get(String name, byte[] code) {
return defineClass(name, code, 0, code.length);
}
}.get("B", finishedClass);
而不是将其注入A
的加载程序。