简体   繁体   中英

Add default constructor in a java class in runtime

I'm writing a lib and I need to create proxies for some objects. Since some classes don't implement any interfaces then I decided to use CGLIB to create proxies instead of JDK proxy. But I faced with situation when some classes don't have default constructor and CGLIB fails to create proxies for those types, ie CGLIB throws exception with message: Superclass has no null constructors but no arguments were given. How I can solve this problem, is there some way to add default constructor in runtime using cglib/asm or some another instrument? Thanks.

Use http://objenesis.org/ . One of its typical use cases exactly addresses your problem:

Proxies, AOP Libraries and Mock Objects - Classes can be subclassed without needing to worry about the super() constructor.

I'll just copy and paste the solution from the blog post provided in the comments to another answer to preserve it. It combines Objenesis and CGLIB and it really works.

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.objenesis.ObjenesisHelper;

public class ProxyPlayground {

    public static void main(final String[] args) {

        final MethodInterceptor hashCodeAlwaysNull = new MethodInterceptor() {

            @Override
            public Object intercept(final Object object, final Method method,
                    final Object[] args, final MethodProxy methodProxy)
                    throws Throwable {
                if ("hashCode".equals(method.getName())) {
                    return 0;
                }
                return methodProxy.invokeSuper(object, args);
            }
        };
        final Foo proxy = createProxy(Foo.class, hashCodeAlwaysNull);
        System.out.println(proxy.hashCode()); // prints 0

    }

    @SuppressWarnings("unchecked")
    private static <T> T createProxy(final Class<Foo> classToMock,
            final MethodInterceptor interceptor) {
        final Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(classToMock);
        enhancer.setCallbackType(interceptor.getClass());

        final Class<?> proxyClass = enhancer.createClass();
        Enhancer.registerCallbacks(proxyClass, new Callback[] { interceptor });
        return (T) ObjenesisHelper.newInstance(proxyClass);
    }
}

It's possible, below code reference from objenesis.

ReflectionFactory reflectionFactory = ReflectionFactory.getReflectionFactory();
Constructor<?> constructor = reflectionFactory.newConstructorForSerialization(YourObject.class, Object.class.getConstructor((Class[]) null));
Object instance = constructor.newInstance();

What you assume to be express as byte code, cannot be done as it would be rejected by the JVM' verifier. As pointed out by the other answer, you should consider using a library such as Objenesis. However, note that Objenesis makes use of JVM-internal APIs what will not longer be possible using Java 9 when project Jigsaw is introduced.

For this reason, you might rather approach the matter differently. Cglib merely copies all constructors of the super class. You want to call any constructor of which you know it is side effect free. Simply pass null values or 0 values for primitives. As long as you intercept all methods, the object state does not matter anyways as none of the real methods are ever invoked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM