I need to generate sub class using cglib for a class that does not have default constructor. I have following code that works fine for class with default constructor:
Enhancer enhancer = new Enhancer();
enhancer.setCallbackType(NoOp.class);
enhancer.setUseCache(false);
enhancer.setSuperclass(clazz);
return enhancer.createClass();
New class should have default constructor which need to call some non default constructor from its super class.
I have searched and found that cglib can not do such things and I need to use asm. However I could not find examples of adding default constructor to class.
If someone has an example how to implement it, that would be great.
I solved this problem. It appeared a bit different than I imagined before. Cglib inherits all constructors and not only default one as I thought before.
However it appeared that I can not replace constructor without affecting existing cglib constructor construction code. This is minor implication, so I just moved from constructor injection to method injection. I am adding my method call just before constructor returns. And this works!!! I am so happy about it.
This is what I got:
cglib Enhancer invocation
Enhancer enhancer = new Enhancer();
enhancer.setNamingPolicy(new IndexedNamingPolicy());
enhancer.setCallbackType(NoOp.class);
enhancer.setUseCache(false);
enhancer.setStrategy(new DefaultGeneratorStrategy() {
@Override
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
return new TransformingClassGenerator(cg, new DefaultConstructorEmitter(key));
}
});
enhancer.setSuperclass(clazz);
return enhancer.createClass();
and my DefaultConstructorEmitter (huh it is still named for constructor processing, never mind)
private class DefaultConstructorEmitter extends ClassEmitterTransformer {
private final Signature CALL_SIGNATURE = TypeUtils.parseSignature("void someMethod(Object)");
private String parametersKey;
public DefaultConstructorEmitter(final String key) {
parametersKey = key;
}
@Override
public CodeEmitter begin_method(int access, Signature sig, Type[] exceptions) {
final CodeEmitter emitter = super.begin_method(access, sig, exceptions);
if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
return new CodeEmitter(emitter) {
@Override
public void visitInsn(int arg0) {
if (arg0 == Opcodes.RETURN) {
Type classType = ...
emitter.load_this();
emitter.push(parametersKey);
emitter.invoke_static(classType, CALL_SIGNATURE);
}
super.visitInsn(arg0);
}
};
}
return emitter;
}
}
Hope this example will help someone not to spent several hours like I did.