grace-spring is the Spring DSL and runtime bean configuration layer of the Grace Framework. It provides the Groovy-based BeanBuilder DSL that plugins use in doWithSpring closures, and the RuntimeSpringConfiguration infrastructure that accumulates bean definitions and registers them with the Spring ApplicationContext.
Only groovy-xml, spring-core, spring-beans, and spring-context are api dependencies. spring-aop, spring-tx, and spring-web are compileOnly — making this a very lightweight module.
grace-spring/src/main/groovy/
├── grails/spring/
│ ├── BeanBuilder.java # Groovy DSL for Spring bean definitions
│ └── DynamicElementReader.groovy # Spring XML namespace support in the DSL
└── org/grails/spring/
├── BeanConfiguration.java # Interface: a single bean's config
├── DefaultBeanConfiguration.java # Implementation of BeanConfiguration
├── RuntimeSpringConfiguration.java # Interface: accumulator for all beans
├── DefaultRuntimeSpringConfiguration.java # Implementation of RuntimeSpringConfiguration
├── GrailsApplicationContext.java # GenericApplicationContext + Groovy access
├── GrailsContextEvent.java # ApplicationEvent for Grace context events
├── RuntimeSpringConfigUtilities.java # Loads spring/resources.groovy
├── TypeSpecifyableTransactionProxyFactoryBean.java
├── aop/autoproxy/ # AOP auto-proxy support
├── beans/factory/ # Bean factory support
├── beans/support/ # Bean support utilities
└── context/ApplicationContextExtension.groovy # Groovy extension methods for ApplicationContext
The central class of the module. It is a GroovyObjectSupport that overrides invokeMethod, getProperty, and setProperty to intercept Groovy method calls and translate them into Spring BeanDefinition registrations. This is what makes the doWithSpring DSL work:
// Inside a Plugin's doWithSpring closure:
myService(MyService) {
dataSource = ref('dataSource') // setProperty → addProperty on BeanConfiguration
timeout = 30
}invokeMethod(name, args)— whennameis a class, creates aBeanConfigurationviaRuntimeSpringConfiguration.addSingletonBean()getProperty(name)— returns aRuntimeBeanReferencefor an already-defined bean (enablingrefsyntax)setProperty(name, value)— sets a property on the currentBeanConfigurationbeans(Closure)— entry point for a bean-defining blockimportBeans(String)— imports beans from XML or Groovy resource patternsxmlns(Map)— registers Spring XML namespace handlers for use in the DSLcreateApplicationContext()— finalizes and refreshes theApplicationContextregisterBeans(BeanDefinitionRegistry)— registers all accumulated beans into an existing registry
Used by BeanBuilder to handle Spring XML namespace expressions (e.g., aop, tx, context) within the Groovy DSL. It uses StreamingMarkupBuilder to serialize the Groovy closure call into an XML element, then delegates to the appropriate Spring NamespaceHandler to parse it.
The interface that BeanBuilder writes into. It accumulates BeanConfiguration objects and BeanDefinition instances, then registers them all with a BeanDefinitionRegistry at once. Key methods:
| Method | Purpose |
|---|---|
addSingletonBean(name, clazz) |
Add a named singleton bean |
addPrototypeBean(name, clazz) |
Add a named prototype bean |
addAbstractBean(name) |
Add an abstract (parent) bean |
addBeanDefinition(name, bd) |
Add a raw Spring BeanDefinition |
getBeanConfig(name) |
Retrieve a BeanConfiguration by name |
containsBean(name) |
Check if a bean is registered |
registerBeansWithRegistry(registry) |
Flush all beans into a BeanDefinitionRegistry |
registerBeansWithContext(ctx) |
Flush all beans into a GenericApplicationContext |
registerBeansWithConfig(target) |
Merge into another RuntimeSpringConfiguration |
addAlias(alias, beanName) |
Register a bean alias |
The concrete implementation. It maintains three internal maps:
beanConfigs—Map<String, BeanConfiguration>for DSL-defined beansbeanDefinitions—Map<String, BeanDefinition>for raw SpringBeanDefinitioninstancesbeanNames—LinkedHashSet<String>preserving insertion order
registerBeansWithRegistry() iterates all three and calls registry.registerBeanDefinition() for each. It creates a GrailsApplicationContext (not a standard GenericApplicationContext) as its backing context.
A fluent builder interface wrapping a Spring AbstractBeanDefinition. It is what BeanBuilder returns when you define a bean, and what RuntimeSpringConfiguration stores internally.
DefaultBeanConfiguration implements it as a GroovyObjectSupport, so property assignments on it (e.g., bean.timeout = 30) are intercepted and stored as PropertyValue entries on the underlying GenericBeanDefinition.
Extends Spring's GenericApplicationContext and implements GroovyObject. This allows beans to be retrieved with dot-dereference syntax in Groovy code (ctx.myService instead of ctx.getBean('myService')). It also:
- Overrides
setPropertyto acceptBeanDefinitionassignments (enabling hot-reload of bean definitions) - Registers the Spring
Environmentunder the namespringEnvironment(instead of the defaultenvironment) to avoid conflicts with Grace's ownEnvironmentclass - Overrides
assertBeanFactoryActive()as a no-op to prevent excessive synchronization
A utility class that handles loading the application's spring/resources.groovy (or spring/resources.xml) file. It compiles the Groovy script, extracts the beans closure, and feeds it into a BeanBuilder. It also caches the BeanBuilder instance for reuse during hot-reload.
A Spring ApplicationEvent published on the WebApplicationContext to signal Grace-specific lifecycle milestones (currently only DYNAMIC_METHODS_REGISTERED = 0).
Extends Spring's TransactionProxyFactoryBean to allow the proxied type to be declared explicitly. This is needed when creating scoped proxies of transactional service beans — the scoped proxy needs to know the type before the transactional proxy has instantiated the underlying service.
graph TD
A["Plugin.doWithSpring closure"] --> B["BeanBuilder.beans(closure)"]
B --> C["invokeBeanDefiningClosure()"]
C --> D["invokeMethod(beanName, [BeanClass, configClosure])"]
D --> E["RuntimeSpringConfiguration.addSingletonBean()"]
E --> F["DefaultBeanConfiguration created"]
D --> G["configClosure.delegate = BeanBuilder"]
G --> H["setProperty(propName, value)"]
H --> I["BeanConfiguration.addProperty()"]
I --> J["GenericBeanDefinition.getPropertyValues().add()"]
B --> K["finalizeDeferredProperties()"]
K --> L["Resolve List/Map → ManagedList/ManagedMap"]
L --> M["GrailsApplicationPostProcessor.postProcessBeanDefinitionRegistry()"]
M --> N["RuntimeSpringConfiguration.registerBeansWithRegistry(registry)"]
N --> O["registry.registerBeanDefinition() for each bean"]
O --> P["Spring ApplicationContext refresh"]
grace-spring is a direct api dependency of 11 modules:
| Module | Usage |
|---|---|
grace-core |
BeanBuilder in AbstractGrailsApplication; RuntimeSpringConfiguration in artefact configuration |
grace-plugin-api |
BeanBuilder is the DSL used in every plugin's doWithSpring closure |
grace-boot |
DefaultRuntimeSpringConfiguration + RuntimeSpringConfigUtilities in GrailsApplicationPostProcessor.postProcessBeanDefinitionRegistry() |
grace-plugin-core |
DefaultRuntimeSpringConfiguration + RuntimeSpringConfigUtilities in CoreGrailsPlugin.onChange() for hot-reload |
grace-web |
Extends BeanBuilder as WebBeanBuilder (uses WebRuntimeSpringConfiguration); GrailsApplicationContext used as the web app context |
grace-web-gsp |
Uses BeanBuilder for GSP-related bean registration |
grace-web-taglib |
Uses BeanBuilder for tag library bean registration |
grace-test-suite-* |
Uses BeanBuilder directly in tests (e.g., DataSourcesGrailsPluginTests) |
The most critical usage is in grace-boot's GrailsApplicationPostProcessor, which creates a DefaultRuntimeSpringConfiguration, passes it to every plugin's doWithSpring via pluginManager.doRuntimeConfiguration(), then calls springConfig.registerBeansWithRegistry(registry) to flush all plugin-contributed beans into the Spring BeanDefinitionRegistry before the context is refreshed.
grace-web extends BeanBuilder with WebBeanBuilder, which overrides createRuntimeSpringConfiguration() to return a WebRuntimeSpringConfiguration (a subclass that creates a GrailsWebApplicationContext instead of a plain GrailsApplicationContext).
In summary, grace-spring provides three pillars: the Groovy Spring DSL (BeanBuilder + DynamicElementReader), the bean accumulator (RuntimeSpringConfiguration + DefaultRuntimeSpringConfiguration), and the Groovy-aware ApplicationContext (GrailsApplicationContext) that makes the entire Grace plugin bean registration system work.