With ByteBuddy You can generate a static initialization block using ByteCodeAppender. This will result in a little different class then You wanted but i think close enough:
public class ConstructedClass {
public static final MyClass a;
static {
a = new MyClass();
}
}
Generation code:
public class ByteBuddyTest {
public static void main(String[] args) throws Exception {
DynamicType.Loaded loaded =
new ByteBuddy()
.subclass(Object.class)
.initializer( new ByteCodeAppender() {
@Override public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext,
MethodDescription instrumentedMethod) {
StackManipulation.Size size = new StackManipulation.Compound(
TypeCreation.of(new TypeDescription.ForLoadedType(MyClass.class)),
Duplication.SINGLE,
MethodInvocation.invoke(new TypeDescription.ForLoadedType(MyClass.class).getDeclaredMethods().filter(ElementMatchers.isDefaultConstructor()).getOnly()),
FieldAccess.forField(implementationContext.getInstrumentedType().getDeclaredFields().filter(ElementMatchers.named("a")).getOnly()).write()
).apply(methodVisitor, implementationContext);
return new Size(size.getMaximalSize(), instrumentedMethod.getStackSize());
}
})
.name("org.test.lib.core.ConstructedClass")
.modifiers(Opcodes.ACC_PUBLIC)
.defineField("a", MyClass.class, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)
.make()
.load(ByteBuddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
Object obj = loaded.getLoaded().getConstructor().newInstance();
System.out.println(obj.getClass().getField("a").get(obj));
}
public static class MyClass {
public MyClass(String arg) {}
public static MyClass createMyClass(String arg) {
return new MyClass(arg);
}
}
}
Update for comment
To call a static factory method instead of constructor You just need to replace constructor call:
StackManipulation.Size size = new StackManipulation.Compound(
new TextConstant("test"),
MethodInvocation.invoke(new TypeDescription.ForLoadedType(MyClass.class).getDeclaredMethods().filter(ElementMatchers.named("createMyClass")).getOnly()),
FieldAccess.forField(implementationContext.getInstrumentedType().getDeclaredFields().filter(ElementMatchers.named("a")).getOnly()).write()
).apply(methodVisitor, implementationContext);
Multiple fields
DynamicType.Builder builder = new ByteBuddy().subclass(Object.class);
List<String> fields = Lists.newArrayList("a", "b", "c");
for (String str : fields) {
builder = builder.defineField(str, MyClass.class, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL);
}
DynamicType.Loaded loaded = builder.make().load(ByteBuddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION);
Object obj = loaded.getLoaded().getConstructor().newInstance();
System.out.println(obj.getClass().getField("a"));
System.out.println(obj.getClass().getField("c"));