Статьи

Groovy Spring Integration: Использование скриптов Groovy в качестве Spring Beans

Поддержка Spring для языков сценариев позволяет вам расширять ваши Java-приложения с помощью bean-компонентов, определенных на языке сценариев, например Groovy. Контейнер Spring прозрачно создает экземпляры, настраивает и устанавливает зависимости между этими поддерживаемыми языками. Бины, определенные в языке сценариев, таком как Groovy, имеют ряд полезных преимуществ, таких как возможность «обновлять» уже загруженные классы Groovy при изменении исходных файлов.

Groovy позволяет писать как классы, так и скриптлеты. Например, ниже приведен отличный скриптлет:

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

Эта статья посвящена тому, как настраивать и использовать такие скриптлеты в Spring из-за их различий с обычными классами Groovy. Он не охватывает более распространенный случай настройки обычных классов Groovy. Для ознакомления с основами поддержки Spring для языков сценариев и интеграции Groovy / Spring, пожалуйста, прочитайте Динамические языковые компоненты в Spring и Spring: Поддержка динамических языков .

Groovy: Скриплет против класса

Давайте сначала определим, что мы имеем в виду под скриптом Groovy. Вот скриптлет (определенный в файле 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}”}

 Вот эквивалентный класс:

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

Приведенный выше код подчеркивает некоторые различия между скриптлетом и классом:

  • Скриплет поддерживается Binding и не должен явно объявлять переменные уровня экземпляра. При первом использовании переменные добавляются в привязку скрипта. Одним из важных отличий здесь является то, что Groovy не создает никаких методов получения / установки для таких переменных привязки. Spring-поиск для установщиков JavaBean завершится неудачно для привязки-свойств-свойств, когда он попытается установить зависимости. 
  • Groovy создает класс и main () для сценария за кулисами. Все операторы, которых нет ни в одном методе, (примерно) становятся частью main (), сгенерированного Groovy для скриптлета.

Весенний пример

Этот раздел добавляет Spring к примеру и показывает, как скрипты Groovy и Java-бины могут быть определены и подключены вместе.

Вот простой Java-бин, который понадобится нашему скриптлету в качестве зависимости:

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

Вот наш скрипт Groovy, которому нужно вставить некоторые зависимости, которые он явно хочет найти, используя контекстную ссылку Spring:

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

Этот скриптлет требует от Spring следующих зависимостей: user1, user2, user3 и springAppContext.

Есть несколько вариантов получения зависимостей из Spring в ваших скриптлетах:

  • Использование явных методов получения / установки: когда Spring пытается внедрить зависимость, он использует отражение Java для поиска установщика JavaBean. Итак, простой вариант — предоставить в вашем скриптлете явные методы установки, а затем просто использовать нотацию <lang: property> для внедрения зависимости в ваш базовый bean-компонент.
  • Использование универсального установщика GroovyObject: Все скрипты Groovy реализуют интерфейс GroovyObject, который предоставляет универсальный метод setProperty (). Его реализация помещает свойство в привязку скрипта. Здесь мы используем интерфейс GroovyObjectCustomizer Spring, чтобы помочь нам вызывать универсальный setter setProperty () вместо отдельных установщиков свойств, которые могут отсутствовать в скриптлетах, если не указано явно.

Вот наша реализация 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;    }}

Что делает наш настройщик, так это то, что он помещает все зависимости скрипта в свою привязку с помощью GroovyObject # setProperty () и таким образом делает их доступными для обычного использования. Кроме того, он также делает контекст приложения доступным для нашего скриптлета с именем «springAppContext», чтобы он мог получать любые другие bean-компоненты из нужного им контекста.

Наконец, вот конфигурация Spring, которая связывает все эти бины Spring / Java вместе:

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

Как можно видеть здесь, зависимость ‘user2’ вводится в скриптлет с использованием его явного набора setTser2 () и нотации <lang: property>, тогда как зависимость ‘user1’ вводится в скриптлет через наш собственный ScriptletCustomizer. ScriptletCustomizer внедряет ‘user1’ в скриптлет с помощью универсального метода setProperty () из groovy, а также вставляет контекстную ссылку Spring ‘springAppContext’, чтобы наш скриптлет мог замыкать любые дополнительные компоненты, которые ему нужны.

На этом статья заканчивается. Использование Binding скриптами groovy отличает их в том, что касается их интеграции, поскольку классы, сгенерированные Groovy для скриптов, не имеют никаких установщиков JavaBean для привязки переменных, и поэтому для настройки их зависимостей необходимы специальные средства.

Код для этой статьи можно скачать здесь .