2

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.

0

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.