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
Submission: On July 04 via manual from BY — Scanned from DE
Form analysis
1 forms found in the DOMGET /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.