Overriding Groovy constructors

It’s rare to create parameterized constructors in Groovy since the Map constructor that’s added to all classes reduces the need for “real” constructors. There’s still a case for defining constructors to enforce business rules, for example if an instance requires certain fields to be set in order for it to be valid. But in general we tend to just use the Map constructor.

But what if you want to override that constructor, replacing its implementation, or adding code before or after the default implementation? Also, Grails augments domain class constructors to support dependency injection from the Spring ApplicationContext, so is it possible to reimplement or augment that behavior?

One thing that’s important to point out is that the Map constructor relies on the default constructor that’s added to all classes that don’t define any explicit constructors. It calls that constructor, then sets properties from the provided Map (this is defined in MetaClassImpl.invokeConstructor() if you’re curious). But if you declare one or more parameterized constructors the compiler doesn’t generate an empty one for you, and the Map constructor fails.

For example, if you run this in a Groovy console, it will fail with an exception:

class Thing {
   Thing(String name) { this.name = name }
   String name
}

def t = new Thing(name: 'x')
println f.name

The fix is to add an empty constructor:

class Thing {
   Thing() {}
   Thing(String name) { this.name = name }
   String name
}

def t = new Thing(name: 'x')
println f.name

or not define any at all:

class Thing {
   String name
}

So how can we replace the Map constructor? It depends on whether we want to redefine the implementation, or add logic before or after the default implementation. Note – for these examples I assume you have a domain class named “User”.

To add behavior, we need to be able to call the constructor that Groovy adds:

def originalMapConstructor = User.metaClass.retrieveConstructor(Map)

User.metaClass.constructor = { Map m ->

   // do work before creation

   def instance = originalMapConstructor.newInstance(m)

   // do work after creation

   instance
}

If you want to redefine the behavior it’s simpler, just do that work in the Closure that will become the constructor:

User.metaClass.constructor = { Map m ->

   // do work before creation

   def instance = new User()

   // do initialization, e.g. "instance.properties = m"

   // do work after creation

   instance
}

Overriding the default constructor is a bit trickier, and only really applies to Grails, and really just domain class constructors. The problem is that it’s nontrivial to access the real default constructor that’s added by the compiler. In fact you can’t, but there’s a hackish workaround for that.

To just add behavior, we need to be able to access the behavior that Grails adds. DomainClassGrailsPlugin overrides the default constructor in its doWithDynamicMethods callback (doing the work in enhanceDomainClasses()) and takes advantage of the fact that domain classes are registered as prototype Spring beans. This means that requesting the associated bean gives you a new instance each time, and since it’s a bean any public fields that correspond to other Spring beans are populated via dependency injection.

So assuming you have access to the GrailsApplication (most likely via dependency injection with "def grailsApplication"), you can do this:

User.metaClass.constructor = { ->

   // do work before creation

   def instance = grailsApplication.mainContext.getBean(User.name)

   // do work after creation

   instance
}

If you want to redefine the behavior, ideally you could call the compiler-generated default constructor. But that’s not available, having been replaced in the MetaClass. So you can use a trick, unfortunately only available in a Sun or JRockit JDK:

import sun.reflect.ReflectionFactory

def objectConstructor = Object.getConstructor()

User.metaClass.constructor = { ->
   def factory = ReflectionFactory.reflectionFactory
   def cleanConstructor = factory.newConstructorForSerialization(
      User, objectConstructor)
   cleanConstructor.accessible = true

   // do work before creation

   def instance = cleanConstructor.newInstance()

   // do work after creation

   instance
}

You can also go further and trigger dependency injection:

import sun.reflect.ReflectionFactory
import org.springframework.beans.factory.config.AutowireCapableBeanFactory

def objectConstructor = Object.getConstructor()

User.metaClass.constructor = { ->
   def factory = ReflectionFactory.reflectionFactory
   def cleanConstructor = factory.newConstructorForSerialization(
      User, objectConstructor)
   cleanConstructor.accessible = true

   // do work before creation

   def instance = cleanConstructor.newInstance()

   // trigger dependency injection
   def ctx = grailsApplication.mainContext
   def beanFactory = ctx.autowireCapableBeanFactory
   beanFactory.autowireBeanProperties(instance,
         AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false)
   
   // do work after creation

   instance
}

Note that since the constructor is bypassed, field initializations won’t run. So for example if your class looks like this:

class User {
   String name
   Integer foo = 5
   def something = new Something()
   def userService
}

then the ‘foo’ and ‘something’ fields will be null. ‘userService’ will be populated only if you retain the default dependency injection behavior or call it yourself.

2 Responses to “Overriding Groovy constructors”

  1. Raj says:

    Thx for this article. One question, where do I set the closure on metaClass.constructor? In the grails domain class definition as a member, in the same file after the class definition or somewhere else like Bootstrap.groovy?

    I am not sure where to put it such that the override is executed during startup or lazy loading of the first instance of the domain class.

Leave a Reply

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.