burtbeckwith.com Open in urlscan Pro
74.208.236.16  Public Scan

URL: https://burtbeckwith.com/blog/?p=1003
Submission: On July 04 via manual from BY — Scanned from DE

Form analysis 1 forms found in the DOM

GET /blog/index.php

<form method="get" id="searchform" action="/blog/index.php">
  <div><input type="text" value="" name="s" id="s">
  </div>
</form>

Text Content

An Army of Solipsists


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.

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

This entry was posted on Sunday, November 13th, 2011 at 1:17am and is filed
under grails, groovy, java. You can follow any responses to this entry through
the RSS 2.0 feed. Both comments and pings are currently closed.


2 RESPONSES TO “OVERRIDING GROOVY CONSTRUCTORS”


 1. Raj says:
    October 8, 2012 at 3:41 pm
    
    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.
    
    * Burt says:
      October 9, 2012 at 12:20 pm
      
      BootStrap.groovy is a good place for this.
    
      
    


« Accessing the GrailsApplication and ApplicationContext from domain classes
without holders
Create your own Grails holder class »


 * SEARCH
   
   


 * PAGES
   
   * About
   * Grails Plugins
 * 
   
 * 
 * 
 * 


 * CATEGORIES
   
   * ant (3)
   * apache (1)
   * appengine (1)
   * database (4)
   * easymock (1)
   * eclipse (7)
   * generics (6)
   * gorm (19)
   * grails (204)
   * grailsplugin (154)
   * groovy (149)
   * hibernate (27)
   * java (238)
   * JavaScript (2)
   * jms (1)
   * junit (3)
   * log4j (7)
   * mysql (2)
   * opensource (2)
   * performance (6)
   * security (18)
   * server logs (18)
   * spring (17)
   * swing (1)
   * ThisWeekInGrails (124)
   * tomcat (1)
   * twitter (1)
   * wtf (1)


 * ARCHIVE
   
   * December 2017
   * March 2016
   * January 2016
   * September 2015
   * January 2014
   * December 2013
   * July 2013
   * May 2013
   * April 2013
   * March 2013
   * February 2013
   * January 2013
   * December 2012
   * November 2012
   * October 2012
   * September 2012
   * August 2012
   * July 2012
   * June 2012
   * May 2012
   * April 2012
   * March 2012
   * February 2012
   * January 2012
   * December 2011
   * November 2011
   * October 2011
   * September 2011
   * August 2011
   * July 2011
   * June 2011
   * May 2011
   * April 2011
   * March 2011
   * February 2011
   * January 2011
   * December 2010
   * November 2010
   * October 2010
   * July 2010
   * February 2010
   * January 2010
   * December 2009
   * October 2009
   * August 2009
   * July 2009
   * June 2009
   * April 2009
   * March 2009
   * February 2009
   * January 2009
   * December 2008
   * November 2008
   * September 2008
   * August 2008
   * July 2008
   * June 2008
   * April 2008
   * March 2008
   * January 2008
   * December 2007
   * November 2007
   * September 2007
   * August 2007
   * March 2007
   * February 2007
   * October 2006
   * September 2006
   * August 2006
   * July 2006


 * CALENDAR
   
   July 2023 M T W T F S S « Dec      12 3456789 10111213141516 17181920212223
   24252627282930 31  


 * BLOGROLL


 * META
   
   * Log in
 * 
   
   
 * 
 * 
 * 
   
 * 
 * 
 * 
   
 * 
 * 

Entries (RSS) and Comments (RSS).
WordPress 4.9.3. 33 queries in 0.229 seconds.


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