简体   繁体   中英

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

Let's say I want to dynamically create class B <: A that I require to be visible everywhere where A is visible.

(Strictly speaking, I want to create class B <: A as an alternative to existing C <: A and require it to be visible everywhere, where C is visible. But that's a detail.)

And if I understood the classloader hierarchy thing correctly, getting B loaded from the same classloader that loaded A should do the trick.

ByteBuddy has this feature of specifying at which classloader to inject a new 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);

I can get the produced bytecode by means of

 byte[] bytecode = loadable.getBytes();

Which I can then modify and reload.

I've run into some issues with that, though: Why doesn't ASM call my ``visitCode``?

and even if I could get that to work, I don't know if that loading process would re-execute the static initialiser (which I want it to and what I was trying to test with the code in the other question) or not.

So the "safer" route would be to get finished bytecode from ASM before loading the class and then directly load that bytecode.

How do I do that, though?

The "obvious" approach would be to get a DynamicType.Unloaded from the byte[] and then use its load method like above.

I cannot seem to find a way to do that, though.

ClassLoaderByteArrayInjector has a very promising name. But it expects a TypeDescriptor , which -- again -- I cannot seem to get for an unloaded class.

How do I pull this off?

Note that for any ClassLoadingStrategy , only the name of a type matters, so you can simply use loadable.getTypeDescription() , eg

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

as long as your subsequent transformations did not change the name. If you are going to use the resulting map, you have to be aware that the TypeDescription does not reflect your changes made after loadable.getBytes() , so you may recreate the type description from the resulting Class<?> , eg via new TypeDescription.ForLoadedType(resultClass) .

Note that the class, you have linked to, has a method taking only a name and byte code, it's just not static , so this documentation promises that

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

should work (I couldn't test it though).

Note that for a lot of cases, eg A never references B and B doesn't access package-private elements of A , you can simply define a class with a derived class loader

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

instead of injecting it into A 's loader.

Note that the ClassLoaderByteArrayInjector is from an early beta version of Byte Buddy and does no longer exist. You are maybe looking for ClassInjector capabilities ?

You can use these injectors but if you modify byte code with Byte Buddy to begin with you can just use the class loading strategies which are built on top of the class injector for the injection strategy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM