Using Groovy Scriptlets as Spring Beans

Spring’s support for scripting languages allows you to extend your Java applications with beans defined in a scripting language, such as Groovy. Spring container transparently instantiates, configure and dependency injects the beans across these supported languages. Beans defined in a scripting language like Groovy come with some handy advantages such as ability to “refresh” the already loaded Groovy classes when the underlying source files change.

Groovy allows you to write classes as well as scriptlets. For example, following is a groovy scriptlet:

[groovy]
println ‘Hello’ // Groovy will create a full class for me behind the scenes
[/groovy]

This article concentrates on how to configure and use such scriptlets in Spring, due to their differences with normal Groovy classes. It does not cover the more common case of configuring normal Groovy classes. For basics of Spring’s support for scripting languages and Groovy/Spring integration, please read Dynamic language beans in Spring and Spring: Dynamic language support.

Groovy: Scriptlet vs Class

Let’s first define what we mean here by a Groovy scriptlet. Here is a scriptlet (defined in a file Manners.groovy):

[groovy]
package groovyspring.script

user1 = new User(name: ‘Mr X’)
user2 = new User(name: ‘Mr Y’)

sayHiAndBye()

def sayHiAndBye() {
println “Hi, ${user1.name}”
println “Bye, ${user2.name}”
}
[/groovy]

Here is an equivalent class:

[groovy]
package groovyspring.script

class Manners {
def user1, user2
static main(args) {
def m = new Manners()
m.user1 = new User(name: ‘Mr X’)
m.user2 = new User(name: ‘Mr Y’)
m.sayHiAndBye()
}
def sayHiAndBye() {
println "Hi, ${user1.name}"
println "Bye, ${user2.name}"
}
}

[/groovy]

The code above highlights some differences between a scriptlet and a class:

* A scriptlet is backed by a Binding and does not have to explicitly declare the instance level variables. Upon first use, the variables are added to the script’s binding. One important difference here is that Groovy does not create any getter/setter for such binding variables. Spring lookup for JavaBean setters will fail for binding-backed-properties when it tries to setup the dependencies.
* Groovy creates the class and main() for the scriptlet behind the scenes. All the statements that are not in any method, (roughly) become part of the main() Groovy generates for the scriptlet.

Spring-ifying the example

This section adds Spring to the example and shows how Groovy scriplets and Java beans can be defined and wired up together.

Here is a simple Java bean that our scriptlet will need as a dependency:

[groovy]
package groovyspring.model;

public class User {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
[/groovy]

Here is our Groovy scriptlet that needs some of the dependencies injected into it and others that it explicitly wants to lookup using spring context reference

[groovy]
package groovyspring.script

import groovyspring.model.User

def sayHiAndBye() {
println "Hi, ${user1.name}, ${user2.name}"

println "Bye, ${springAppContext.getBean(‘user3’).name}"
}

void setUser2(User user2) {
binding.setVariable(‘user2’, user2)
}

this // the scriplet needs to explicitly return a reference to itself to Spring
[/groovy]

This scriptlet needs from Spring the following dependencies: user1, user2, user3, and springAppContext.

There are a few options for getting the dependencies from Spring injected into your scriptlets:

* Using explicit getters / setters: When Spring tries to inject a dependency, it uses Java reflection to lookup for a JavaBean setter. So, a simple option is to provide in your scriptlet explicit setter methods and then simply use notation to inject the dependency in your groovy bean.
* Using GroovyObject’s generic setter: All Groovy scripts implement interface GroovyObject that provides a generic setProperty() method. Its implementation pushes the property into the script’s binding. We use here Spring’s GroovyObjectCustomizer interface to help us invoke the generic setter setProperty() instead of individual property setters, which may be missing in the scriptlets, if not explicitly provided.

Here is our implementation of GroovyObjectCustomizer:

[groovy]
package groovyspring.script;

import groovy.lang.GroovyObject;
import org.springframework.context.*;
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
import org.springframework.beans.BeansException;
import java.util.List;

public class ScriptletCustomizer implements GroovyObjectCustomizer, ApplicationContextAware {
String[] bindingVars = null;
ApplicationContext applicationContext = null;

public void customize(GroovyObject groovyObject) {
groovyObject.setProperty("springAppContext", applicationContext);

for(String bindingVar : bindingVars) {
groovyObject.setProperty(bindingVar, applicationContext.getBean(bindingVar));
}
}

public void setBindingVars(String[] bindingVars) {
this.bindingVars = bindingVars;
}

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
[/groovy]

What our customizer does is that it pushes all the scritplet’s dependencies into its binding using the GroovyObject#setProperty() and that way makes them available for normal use. In addition, it also makes the app context available to our scriptlet with the name “springAppContext”, so that it can get any other beans from the context that it needs.

Finally, here is Spring configuration that binds all these Spring/Java beans together:

[xml]
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd">

<lang:groovy id="scriptlet1"
script-source="classpath:groovyspring/script/Scriptlet1.groovy"
customizer-ref="scripletCustomizer">
<lang:property name="user2" ref="user2"/>
</lang:groovy>

<bean id="user1" class="groovyspring.model.User">
<property name = "name" value = "Mr X"/>
</bean>

<bean id="user2" class="groovyspring.model.User">
<property name = "name" value = "Mr Y"/>
</bean>

<bean id="user3" class="groovyspring.model.User">
<property name = "name" value = "Mr Z"/>
</bean>

<bean id="scripletCustomizer" class="groovyspring.script.ScriptletCustomizer">
<property name="bindingVars" value="user1"/>
</bean>
</beans>

[/xml]

As can be seen here, the dependency ‘user2’ is injected into the scriptlet using its explicit setter setUser2() and notation, whereas dependency ‘user1’ is injected into the scriptlet via our custom ScriptletCustomizer. ScriptletCustomizer injects ‘user1’ into the scriptlet using groovy’s generic setter setProperty() and also injects the Spring context reference ‘springAppContext’, so that our scriptlet can loopkup any additional beans that it needs.

This concludes the article. The use of Binding by the groovy scripts makes them different as far as their integration is concerned because the classes generated by Groovy for the scripts don’t have any JavaBean setters for binding variables and therefore special means are needed to setup their dependencies.

The code for this article can be downloaded from here.

*Republished from dzone

Roshan Dawrani

Roshan Dawrani has more than 10 years experience in the IT industry and has served on a number of Java projects in prestigious software services and product development companies like Infosys Technologies and Manhattan Associates. An open source enthusiast, Roshan is a keen follower of the dynamic languages for the JVM and an active member of Groovy language’s development team.

One thought on “Using Groovy Scriptlets as Spring Beans

Leave a Reply