Search code examples
javajava-bytecode-asmcglib

generate subclass of class without default constructor


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.


Solution

  • 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.