Configure Morphia to work without a default constructor

In my current project we’re migrating our existing entity model to MongoDB and have turned to Morphia for mapping our Java objects to and from its MongoDB representation. Our Java entities are immutable and preferably we’d like to keep it that way. Morphia, like most other mapping frameworks, require us to have a default constructor but it doesn’t require it to be public. So a work-around would be to have a private constructor that’s used only by Morphia:

public class MyEntity {
    @Id
    private ObjectId id;
    private final List something;

    // For Morphia
    private MyEntity() {
        something = null;
    }

    public MyEntity(List something) {
        this.something = something;
    }

    public List getSomething() {
        return Collections.unmodifiableList(something);
    }
}

It’s quite unfortunate that we have to clutter all our entity models with a private constructor that we really don’t want so what are our options? On the bright side Morphia allows us to configure a lot of things and one of these is the ability to specify a new ObjectFactory. The ObjectFactory is responsible for instantiating Java objects in Morphia. If you don’t want to implement all methods in the interface yourself it’s possible to extend the DefaultCreator and override the createInstance method. Next we’re going to make use of a trick in the JVM that allows us to instantiate an object without invoking any constructor:

Class classToInstantiate = ..
Object instance = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, Object.class.getDeclaredConstructor(null)).newInstance(null);

ReflectionFactory is located in the sun.reflect package so it’s not guaranteed to work in other JVM’s than SUN/Oracle but other vendors does implement this class as well. If you want to be on the safe side you should use the Objenesis framework that deals solely with this issue.

Now since the constructor is not invoked the state inside the object is left uninitialized (all fields are null). For the entities themselves this is no problem since Morphia will set the internal state using reflection after the constructor is invoked but the problem is that the state itself is also constructed using the same ObjectFactory. In the example above it means that the List called something will not be initialized properly. What we can do to solve this is to simply check if the class has a default constructor and if so, use it! We can implement this check in a method like this:

private Constructor getNoArgsConstructor(final Class ctorType) {
        try {
            Constructor ctor = ctorType.getDeclaredConstructor();
            ctor.setAccessible(true);
            return ctor;
        } catch (NoSuchMethodException e) {
            return null;
        }
}

Putting it all together and we have our own custom ObjectFactory:

public class CustomMorphiaObjectFactory extends DefaultCreator {
    @Override
    public Object createInstance(Class clazz) {
        try {
            final Constructor constructor = getNoArgsConstructor(clazz);
            if(constructor != null) {
                return constructor.newInstance();
            }
            try {
                return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(clazz, Object.class.getDeclaredConstructor(null)).newInstance(null);
            } catch (Exception e) {
                throw new MappingException("Failed to instantiate " + clazz.getName(), e);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Constructor getNoArgsConstructor(final Class ctorType) {
        try {
            Constructor ctor = ctorType.getDeclaredConstructor();
            ctor.setAccessible(true);
            return ctor;
        } catch (NoSuchMethodException e) {
            return null;
        }
    }
}

There’s only one thing missing, how do we tell Morphia to use our ObjectFactory? Here’s one way:

Morphia morphia = new Morphia();
Mapper mapper = morphia.getMapper();
mapper.getOptions().objectFactory = new CustomMorphiaObjectFactory();

That’s it! Now you can remove the redundant default constructor from all your entities.

This Post Has 7 Comments

  1. Graciano

    Thanks Johan. Exactly what I was looking for.

  2. Kris

    Great post, great to get rid of redundant code only needed for third party frameworks!

  3. Jakub

    Great post, thanks a lot, works like a charm.
    That’s exactly what I needed.

    Thanks Johan.

  4. Sanders

    Great job. I had a similar situation, so you just save me a lot of time :)

  5. CydrickT

    You saved me! I was considering using Jackson for serialization to a Map and then convert it to a Document, but it necessitate a default constructor too.

    1. Johan Haleby

      Thanks for your kind words

    2. hadix lin

      I have the same requirement. tell me the solution please . Could you show me some sampe codes ?

Leave a Reply