使用特定的类加载器从``byte []``加载一个类

时间:2018-02-23 10:23:51

标签: java bytecode instrumentation java-bytecode-asm byte-buddy

我们想要动态创建我需要在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,再次 - 我似乎无法获得卸载的课程。

如何解决此问题?

1 个答案:

答案 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从不引用BB不访问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的加载程序。