Create your own Grails holder class
Sunday, November 13th, 2011I mentioned in an earlier post that using Grails
*Holder
classes should be avoided, and showed how to access the GrailsApplication
, ApplicationContext
, etc. from domain classes. But there are still cases where you need access to these objects and dependency injection and the approach I described aren’t sufficient. When all else fails you can always create your own holder class.
The reason this works in your application but not in Grails is that the static fields in your application are only accessed within your application. By using shared jars, multiple applications share static state held by shared classes, but this isn’t a problem for your code.
Since everything is reachable from the ApplicationContext
and it’s easy to access, we’ll create a holder for that and expose the other objects from there.
Create a class called ApplicationContextHolder
that implements ApplicationContextAware:
package com.yourcompany.yourapp import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware @Singleton class ApplicationContextHolder implements ApplicationContextAware { private ApplicationContext ctx void setApplicationContext(ApplicationContext applicationContext) { ctx = applicationContext } static ApplicationContext getApplicationContext() { getInstance().ctx } }
and register it in resources.groovy so the ApplicationContext
is injected (using the factoryMethod
attribute since the class is a singleton):
import com.yourcompany.yourapp.ApplicationContextHolder beans = { applicationContextHolder(ApplicationContextHolder) { bean -> bean.factoryMethod = 'getInstance' } }
Now you can use the ApplicationContext
from anywhere to access Spring beans. We can go further though, adding utility methods to expose other classes held by holders:
static Object getBean(String name) { getApplicationContext().getBean(name) } static GrailsApplication getGrailsApplication() { getBean('grailsApplication') } static ConfigObject getConfig() { getGrailsApplication().config } static ServletContext getServletContext() { getBean('servletContext') } static GrailsPluginManager getPluginManager() { getBean('pluginManager') }
You can also add the ability to register mock beans for unit tests; here’s the complete class:
package com.yourcompany.yourapp import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware import javax.servlet.ServletContext import org.codehaus.groovy.grails.commons.GrailsApplication import org.codehaus.groovy.grails.plugins.GrailsPluginManager import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware @Singleton class ApplicationContextHolder implements ApplicationContextAware { private ApplicationContext ctx private static final Map <String, Object> TEST_BEANS = [:] void setApplicationContext(ApplicationContext applicationContext) { ctx = applicationContext } static ApplicationContext getApplicationContext() { getInstance().ctx } static Object getBean(String name) { TEST_BEANS[name] ?: getApplicationContext().getBean(name) } static GrailsApplication getGrailsApplication() { getBean('grailsApplication') } static ConfigObject getConfig() { getGrailsApplication().config } static ServletContext getServletContext() { getBean('servletContext') } static GrailsPluginManager getPluginManager() { getBean('pluginManager') } // For testing static void registerTestBean(String name, bean) { TEST_BEANS[name] = bean } // For testing static void unregisterTestBeans() { TEST_BEANS.clear() } }
Note that the servletContext
and pluginManager
beans are available in a running application but not the Grails console.