I\'m trying to generate classes with a cyclic class dependency, similar to this question: Byte Buddy - Handling cyclic references in generated classes
As a minimal e
This is a working solution, following the accepted answer:
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.modifier.ModifierContributor;
import net.bytebuddy.description.modifier.TypeManifestation;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.jar.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.util.List;
class TypeDescrFix extends TypeDescription.Latent {
TypeDescrFix(final String name, final int modifiers, final Generic superClass, final List<? extends Generic> interfaces) {
super(name, modifiers, superClass, interfaces);
}
@Override
public int getSegmentCount() {
return 0;
}
@Override
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Empty();
}
}
public class ByteBuddyHello {
public static void main(String[] args) {
try {
final ByteBuddy bb = new ByteBuddy();
final TypeDescription.Latent typeDescrA = new TypeDescrFix("A", 0, null, null);
final TypeDescription.Latent typeDescrB = new TypeDescrFix("B", 0, null, null);
final DynamicType.Unloaded<Object> madeA = bb
.subclass(Object.class)
.name("A")
.modifiers(ModifierContributor.Resolver.of(Visibility.PUBLIC, TypeManifestation.FINAL).resolve())
.defineField("theB", typeDescrB, Opcodes.ACC_PUBLIC)
.make();
final DynamicType.Unloaded<Object> madeB = bb.subclass(Object.class)
.name("B")
.modifiers(ModifierContributor.Resolver.of(Visibility.PUBLIC, TypeManifestation.FINAL).resolve())
.defineField("theA", typeDescrA, Opcodes.ACC_PUBLIC)
.make();
Object a = madeA
.include(madeB)
.load(ByteBuddyHello.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded().newInstance();
System.out.println(a.toString());
final File folder = new File("/tmp/ByteBuddyHello");
madeA.saveIn(folder);
madeB.saveIn(folder);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
This is a bug in Byte Buddy; the resolver for type annotations needs to know the depth of any type description and therefore resolves the type path of any type description. For latent types, this depth is always 0 but the default implementation applies a more complex solution.
This will be fixed in the next release. In the meantime, subclass the latent type description and override the method to return 0.
I decided not to change the TypeDescription.Latent
type but rather make the InstrumentedType.Default
implementation more accessible. Use the latter type which allows you to define features of the cyclic type which will be visible to any user. This way, you can for example specify existing fields and methods if you want to define Implementation
s that validate against this feature.