The Basics
Grails plugins allow you to register dynamic methods with any Grails managed or other class at runtime. New methods can only be added within a
doWithDynamicMethods
closure of a plugin.
For Grails managed classes like controllers, tag libraries and so forth you can add methods, constructors etc. using the
ExpandoMetaClass mechanism by accessing each controller's
MetaClass:
class ExamplePlugin {
def doWithDynamicMethods = { applicationContext ->
application.controllerClasses.each { controllerClass ->
controllerClass.metaClass.myNewMethod = {-> println "hello world" }
}
}
}
In this case we use the implicit application object to get a reference to all of the controller classes' MetaClass instances and then add a new method called
myNewMethod
to each controller.
Alternatively, if you know before hand the class you wish the add a method to you can simple reference that classes
metaClass
property:
class ExamplePlugin { def doWithDynamicMethods = { applicationContext ->
String.metaClass.swapCase = {->
def sb = new StringBuffer()
delegate.each {
sb << (Character.isUpperCase(it as char) ?
Character.toLowerCase(it as char) :
Character.toUpperCase(it as char))
}
sb.toString()
} assert "UpAndDown" == "uPaNDdOWN".swapCase()
}
}
In this example we add a new method
swapCase
to
java.lang.String
directly by accessing its
metaClass
.
Interacting with the ApplicationContext
The
doWithDynamicMethods
closure gets passed the Spring
ApplicationContext
instance. This is useful as it allows you to interact with objects within it. For example if you where implementing a method to interact with Hibernate you could use the
SessionFactory
instance in combination with a
HibernateTemplate
:
import org.springframework.orm.hibernate3.HibernateTemplateclass ExampleHibernatePlugin { def doWithDynamicMethods = { applicationContext -> application.domainClasses.each { domainClass -> domainClass.metaClass.static.load = { Long id->
def sf = applicationContext.sessionFactory
def template = new HibernateTemplate(sf)
template.load(delegate, id)
}
}
}
}
Also because of the autowiring and dependency injection capability of the Spring container you can implement more powerful dynamic constructors that use the application context to wire dependencies into your object at runtime:
class MyConstructorPlugin { def doWithDynamicMethods = { applicationContext ->
application.domainClasses.each { domainClass ->
domainClass.metaClass.constructor = {->
return applicationContext.getBean(domainClass.name)
}
} }
}
Here we actually replace the default constructor with one that looks up prototyped Spring beans instead!