Groovy Spring Integration: Using Groovy Scriptlets as Spring Beans

DZone 's Guide to

Groovy Spring Integration: Using Groovy Scriptlets as Spring Beans

Learn how to use Groovy scriptlets as Spring beans.

· Java Zone ·
Free Resource

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:

println 'Hello' // Groovy will create a full class for me behind the scenes

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):

package groovyspring.scriptuser1 = new User(name: ‘Mr X’)user2 = new User(name: ‘Mr Y’)sayHiAndBye()def sayHiAndBye() {println “Hi, ${user1.name}”println “Bye, ${user2.name}”}

 Here is an equivalent class:

package groovyspring.scriptclass 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}"    }   }

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:

package groovyspring.model;public class User {    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

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:

package groovyspring.scriptimport groovyspring.model.Userdef 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

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 <lang:property> 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:

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;    }}

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 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>

As can be seen here, the dependency 'user2' is injected into the scriptlet using its explicit setter setUser2() and <lang:property> 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.

groovy, groovy spring

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}