Spring Dynamic Language Support and a Groovy DSL

Since the introduction of Spring dynamic laguage support in Spring 2.0 it has been an attractive integration point for Groovy, and Groovy provides a rich environment for defining Domain Specific Languages (DSL). But the examples of Groovy integration in the Spring reference manual are limited in scope and do not show the features in Spring that are targeted at DSL integration. In this article I show how to use those features and as an example we add bean definitions to an existing ApplicationContext with a Groovy DSL from the Grails distribution.
Groovy Beans
The basic features of Spring dynamic language integration are exposed in the "lang" namespace in XML. The most straightforward thing you can do is to defined a Spring component as a Groovy bean, in a separate file or inline in the XML. This feature is covered in the Spring reference guide (http://static.springframework.org/spring/docs/2.5.x/reference/index.html) so we don't need to go into too much detail, but for completeness we might as well look at a quick example.
Suppose we have a Java interface
Here is an inline bean definition in Groovy that implements the interface
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<lang:groovy id="messenger">
<![CDATA[
class GroovyMessenger implements spring.Messenger {
def String message;
}
]]>
</lang:groovy>
</beans>
Note that since Groovy defines public getters and setters for all attributes, we do not need to actually write the getMessage() method explicitly. Also remember that a feature of the Spring dynamic language support is that the inline Groovy code can also be pulled out into a separate source file (using the script-source attribute of the lang:groovy element).
Another feature of the Spring dynamic language support is that the script can go beyond simply defining a class. You can also write a Groovy script that does some processing and at the end returns an instance of an object. For example, if we already have an implementation of Messenger called JavaMessenger:
<![CDATA[
def messenger = new JavaMessenger("Hello World!")
messenger
]]>
</lang:groovy>
This has the effect of exposing an instance of the JavaMessenger with a specific message – a trivial example, but a good way to see the feature exposed. Using this technique allows us to go a step beyond normal bean creation patterns in Spring and do as much processing as we like in the script before returning the object.
Under the hood Spring is creating an instance of groovy.util.Script whose run() method returns the object at the end of the script. This will turn out to be important when we start to think about how to integrate a DSL.
Customizing the Groovy Object
The next feature we need to look at to get us into the DSL arena is the ability to customize the Groovy object before it is exposed as a Spring component. This feature was added, I believe, after a meeting between Rod Johnson and Guillaume Laforge at a conference close to the beginning of the Spring 2.0 release (it wasn't in 2.0). Guillaume's interest in Domain Specific Languages led him to observe that Spring is in a good position to be able to manipulate and add behaviour to a Groovy object (or its class) before anyone gets a chance to use it, and since Groovy is a dynamic language this is quite a powerful idiom.
The mechanism they came up with is the GroovyObjectCustomizer interface, which can be applied to the Groovy object before it is exposed to the Spring container client. The interface looks like this:
void customize(GroovyObject goo);
}
and it is applied to the Groovy object after instantiation and (if it is a Script) before it is run. This allows us to play games with the object's methods and properties before it is released.
To apply the customizer all we need to do is add a reference to it in the Groovy bean definition:
<bean id="customizer" class="…"/>
A Domain Specific Language – BeanBuilder
Grails has a nice DSL for Spring components called a BeanBuilder (see here for more details). It allows us to build a Spring ApplicationContext in Groovy in quite a natural and succinct way. According to Graeme Rocher, in recent versions of Grails, BeanBuilder also works without any dependency on web frameworks – you just need Grails Core and Groovy on your classpath. So now is a good time to see if we can integrate the BeanBuilder with Spring (as has also been pointed out on the Spring Forum here). (I actually couldn't get the sample to work with Grails 1.0-rc1 without the servlet API and Spring webflow jars, but probably it will work in rc2 or 1.0 final.)
An expression in a Domain Specific Language in Groovy usually takes the form of a closure, so it would be natural to use the Script pattern from the Spring integration to define the closure. In the case of the BeanBuilder it would look like this:
<![CDATA[
beans = {
messenger(JavaMessenger) {
message = "Hello World!"
}
// ... more bean definitions here ...
}
]]>
</lang:groovy>
This produces a Script object, which itself returns a closure (called "beans") containing bean definitions. One of the bean definitions is our friend the messenger. What we would ideally like is to be able to take those bean definitions and merge them with the current ApplicationContext. To do this we will need to use a GroovyObjectCustomizer.
A Basic GroovyObjectCustomizer
Here is the bare bones of a customizer that will take the closure from a scripted Groovy object and create an application context from it:
public void customize(GroovyObject goo) {
createApplicationContext(goo.run())
}
private ApplicationContext createApplicationContext(Closure value) {
BeanBuilder builder = new BeanBuilder()
builder.beans(value)
builder.createApplicationContext()
}
}
It doesn't do anything with the application context it creates yet – just creates it and lets it evaporate. It also doesn't do any error checking, but we can add that later. The customizer is written in Groovy, so that we can just call goo.run() without casting to Script.
Improved GroovyObjectCustomizer
Now let's improve the implementation so we transfer the bean definitions from the BeanBuilder to the enclosing ApplicationContext.
public void customize(GroovyObject goo) {
addbeanDefinitions(createApplicationContext(goo.run()))
}
private void addBeanDefinitions(ApplicationContext context) {
DefaultListableBeanFactory scriptBeanFactory = context.autowireCapableBeanFactory
for (name in scriptBeanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = scriptBeanFactory.getBeanDefinition(name)
applicationContext.autowireCapableBeanFactory.registerBeanDefinition(name, definition)
}
}
// createAppicationContext defined here….
}
What could be simpler?
Putting it all together so far, we can load this Spring configuration:
<lang:groovy id="beans" customizer-ref="customizer">
<![CDATA[
beans = {
messenger(JavaMessenger) {
message = "Hello World!"
}
// ... more bean definitions here ...
}
]]>
</lang:groovy>
<bean id="customizer" class="BeanBuilderClosureCustomizer"/>
</beans>
and then get the messenger out and use it. In the sample (see attachments) we let the Spring 2.5 TestContextFramework take care of creating an ApplicationContext and injecting dependencies into the test case (so no need for any dependency lookup).
Using the Current Context as a Parent
As a final tweak to make our BeanBuilderClosureCustomizer more useful we will modify it to use the enclosing ApplicationContext as a parent for the bean definitions in the BeanBuilder. To do this we just need a reference to the parent in our customizer, so we need to implement ApplicationContextAware and use that reference to construct the BeanBuilder:
ApplicationContextAware {
def ApplicationContext applicationContext;
public void customize(GroovyObject goo) {
addbeanDefinitions(createApplicationContext(goo.run()))
}
private ApplicationContext createApplicationContext(Closure value) {
BeanBuilder builder = new BeanBuilder(applicationContext)
builder.beans(value)
builder.createApplicationContext()
}
// addBeanDefinitions defined here….
}
Since BeanBuilderClosureCustomizer is written in Groovy we don't need to define explicit getters and setters for the applicationContext property – they are generated automatically by Groovy.
The BeanBuilderClosureCustomizer is now ready for use (with some additional error checking maybe). And the really great thing about Groovy is that it can be compiled and shipped as JVM bytecode in a jar file. So all that I need to do for that is make sure that the generated class file is shipped when my project is packaged. The sample does this just by compiling the Groovy bean into the same target directory as the Java compiler is using.
Referring to Beans in the Parent Context
It would also be pretty neat to refer to beans in the parent context inside our Groovy DSL. Grails allows us to do this already by using the "ref" keyword in the BeanBuilder DSL, e.g.
<










Graeme Rocher says:
Added on November 30th, 2007 at 7:33 amHi Dave,
Nice post, just a few corrections/comments. We have created a separate jar called grails-spring-*.jar that contains only the BeanBuilder and its supporting classes. So you no longer need Grails core. Also it does now work without the servlet dependencies and there are two BeanBuilders: BeanBuilder and WebBeanBuilder. The latter extends BeanBuilder and takes a ServletContext etc.
All of this will be available officially in Grails 1.0 RC2, which will be out in the next week or so.
Also it should be noted that you can use BeanBuilder standalone without the need to embedd within XML if you want to take the Groovy DSL use further.
Again, great post.
Cheers,
Graeme
Stefan Scheidt says:
Added on December 3rd, 2007 at 3:41 amHi Dave,
thanks for this interesting post. I'm quite interested in using Grails BeanBuilder, mainly in combination with XML-based bean definition files, e.g. for aop/tx-namespace based configuration of AOP or TX. What I would like most is something like JavaConfigs ConfigurationBeanPostProcessor for BeanBuilder. That would be even nicer then your suggestion, I think.
Regards and again thanks for your post!
Stefan
Alex says:
Added on June 8th, 2008 at 10:42 pmGreat post and very useful knowledge!
BeanBuilder's DSL offers very comfortable way to define beans.
I tried sample and it works just fine!
But adding into spring configurations reveals some problems in the framework: it can not create customizer bean and fails with "Requested bean is currently in creation: Is there an unresolvable circular reference?" exception.
I tried the last Spring snapshot (2.5.5 20080606) – result is the same.
So, currently there is no way for use DSL together with AOP.
Thank you,
Alex
Alex says:
Added on June 8th, 2008 at 10:46 pmBlog engine eliminated the tag from my previous comment: <aop:config/>
Dave Syer (blog author) says:
Added on June 9th, 2008 at 1:59 amIf you could create a simple example and post it in a JIRA issue, someone could have a look at the circular reference problem. (If you do that, please post back the JIRA issue number here as well.)
Alex says:
Added on September 3rd, 2008 at 12:43 amThe issue SPR-4903 has been resolved (in 2.5.5-20080630-542) and the problem with circular reference disappeared. Spring community was really fast to deal with!
Another problem I've observed with BeanBuilder DSL combined with XML ApplicationContext is double instantiation of the beans described by DSL inside XML. If you insert some debug output to JavaMessenger constructor you will see what I mean.
That's happening because of BeanBuilder's createApplicationContext method calls refresh() on created context and there is no way to call this method without context refreshing (BeanBuilder's API lack?).
I resolved this problem by overriding BeanBuilder's createRuntimeSpringConfiguration method to return my own implementation (extension) of DefaultRuntimeSpringConfiguration which in its turn skips refresh() in getApplicationContext:
protected RuntimeSpringConfiguration createRuntimeSpringConfiguration(
ApplicationContext parent, ClassLoader classLoader) {
return new MyRuntimeSpringConfiguration(parent, classLoader);
}
}
public MyRuntimeSpringConfiguration(ApplicationContext parent, ClassLoader classLoader) {
super(parent, classLoader);
}
public ApplicationContext getApplicationContext() {
initialiseApplicationContext();
registerBeansWithContext(context);
//context.refresh();
return context;
}
}
Thanks!