51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

在 Spring 中使用 Groovy

1、概览 {#1概览}

Groovy 是一种功能强大的动态 JVM 语言,具有众多特性。在 Spring 中使用 Groovy 可以大大提高应用程序的灵活性和可读性。从 Spring 4 开始 支持基于 Groovy 的配置

在本教程中,我们将了解 Groovy 与 Spring 结合使用的各种方法。首先介绍了如何使用 Spring 提供的多个选项来创建 Groovy Bean 定义。接下来,了解如何使用 Groovy 脚本加载 Application Context。最后,学习如何使用 XML 和 GroovyScriptEngine 类将 Groovy 作为脚本执行(无需编译)。

2、Maven 依赖 {#2maven-依赖}

首先,在 pom.xml 中定义 Groovy 依赖:

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>3.0.12</version>
</dependency>

此外,还需要添加 GMavenPlus 插件来编译 Groovy 文件:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>1.9.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>addSources</goal>
                        <goal>addTestSources</goal>
                        <goal>generateStubs</goal>
                        <goal>compile</goal>
                        <goal>generateTestStubs</goal>
                        <goal>compileTests</goal>
                        <goal>removeStubs</goal>
                        <goal>removeTestStubs</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

3、Bean 定义 {#3bean-定义}

传统上,开发人员通过 XML 配置来声明 Bean。这种方式后来被通过 Java 注解以编程方式定义 Bean 所取代。另一种声明 Bean 的方式是通过 Groovy 脚本。

由于我们使用的是 GMavenPlus 插件,Groovy 源文件可以与 src/main/java 源文件夹中的其他 Java 代码混合在一起。不过,最好还是将 Groovy 文件放在专用的 src/main/groovy 源文件夹中,以免后期出现混乱。

3.1、使用 Groovy Bean Builder {#31使用-groovy-bean-builder}

Groovy Bean Builder 是 Java 基于 @Configuration 注解的配置和基于 XML 的配置的强大替代品。如下,是一些基本的 Bean 定义的 Groovy 代码:

beans {
    
    // 声明一个简单的 Bean,带有一个构造函数参数
    company(Company, name: 'ABC Inc');

    // 可以使用更简单的语法声明相同的 bean:beanName(type, constructor-args)
    company String, 'ABC Inc'

    // 声明一个 employee 对象,通过其 setter 方法引用前一个 Bean
    employee(Employee) {
        firstName = 'Lakshmi'
        lastName = 'Priya'
        // 引用其他 bean 的方式有两种
        vendor = company // 或者: vendor = ref('company')
    }
    // 允许导入其他配置文件,包括 XML 和 Groovy 文件
    importBeans('classpath:ApplicationContext.xml')
    importBeans('classpath:GroovyContext.groovy')
}

如上,包含了所有 Bean 声明的 beans 结构是一个闭包,GroovyBeanDefinitionReader 将其作为 DSL 来处理。

3.2、使用注解 {#32使用注解}

Groovy 类也可以是有效的 Spring Bean,在基于注解的配置中,可以用 Groovy 代替 Java:

@Configuration
class SpringGroovyConfiguration{
    
    @Bean
    List<String> fruits() {
        ['Apple', 'Orange', 'Banana', 'Grapes']
    }

    @Bean
    Map<Integer, String> rankings() {
        [1: 'Gold', 2: 'Silver', 3: 'Bronze']
    }
}

3.3、使用 XML {#33使用-xml}

Groovy Bean Builder 和基于注解的配置都更加灵活。不过,我们仍然可以使用 XML 来声明在 Groovy 脚本中定义的 Bean。Groovy 是一种 动态语言,Spring 为其提供了全面的支持。首先需要在 XML 配置中使用一个特殊元素(<lang:groovy>)来表示正在定义动态语言支持的 Bean。

例如,如下 XML 配置示例,它引用了正确的 schema,所以可以使用 lang 命名空间中的标签:

<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 https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang https://www.springframework.org/schema/lang/spring-lang.xsd">
    <lang:groovy id="notification" script-source="file:NotificationServiceImpl.groovy" refresh-check-delay="10000" >
        <lang:property name="message" value="Hello" />
    </lang:groovy>
</beans>

这里通过 script-source 属性声明了指向 Groovy 脚本的 notification Bean。可以使用 file 前缀指定脚本的确切位置。或者,也可以使用 classpath 前缀直接从 classpath 访问资源。refresh-check-delay 属性定义了脚本的刷新间隔,当脚本内容发生变化时,可以自动刷新。

4、加载 Application Context {#4加载-application-context}

Spring 需要知道如何加载 Groovy Context 文件,以使应用程序可以使用 Bean。可以通过在 web.xml 中进行配置或以编程方式加载 Context 来实现这一点。

4.1、在 web.xml 中添加 Groovy 配置 {#41在-webxml-中添加-groovy-配置}

Spring 4.1 使用 GroovyWebApplicationContext 增加了通过 web.xml 加载 Groovy 配置文件的支持。

默认情况下,配置将从 /WEB-INF/applicationContext.groovy 加载。这可以通过 contextConfigLocation Servlet Context Parameter 重写该位置:

<web-app>
    
    ...
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.GroovyWebApplicationContext</param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/applicationContext.groovy</param-value>
    </context-param>
    
    ...
</web-app>

4.2、使用 GenericGroovyApplicationContext {#42使用-genericgroovyapplicationcontext}

Spring 提供了 GenericGroovyApplicationContext 来加载 Groovy Bean 定义。此外,该 Context 可通过内联 Bean 定义闭包加载:

def context = new GenericGroovyApplicationContext()
context.reader.beans {
    department(Department) {
        name = 'Finance'
        floor = 3
    }
}
context.refresh()

或者,可以将此 Bean 定义外部化,并从 Groovy 配置文件中加载 Application Context:

GenericGroovyApplicationContext context = new GenericGroovyApplicationContext();
context.load("config/applicationContext.groovy");
context.refresh();

可以看到,加载 Groovy Context 的风格,与实例化 Java XmlWebApplicationContextClassPathXmlApplicationContext 的风格类似。

在没有额外配置的情况下,代码可以更加简洁:

ApplicationContext context = new GenericGroovyApplicationContext("config/applicationContext.groovy");
String foo = context.getBean("foo", String.class);

此外,GenericGroovyApplicationContext 还能理解 XML Bean 定义文件。这样就能与 Groovy Bean 定义文件进行无缝混合和匹配,从而增加了灵活性。

5、执行 Groovy 脚本 {#5执行-groovy-脚本}

除了 Groovy Bean 定义,Spring 还支持在不编译的情况下执行 Groovy 脚本。这种执行方式可以是独立的 Bean,也可以是在 Bean 中调用 Groovy 脚本,使脚本成为 Bean 的可执行部分。

5.1、内联脚本 {#51内联脚本}

如前所述,可以使用 Spring 提供的动态语言支持,将 Groovy 源文件直接嵌入 Spring Bean 定义中。因此,可以使用 <lang:inline-script/> 元素在 Spring 配置 XML 文件中定义 Groovy 源文件。

例如,可以使用内联脚本功能创建一个 Notifier Bean:

<lang:groovy id="notifier">
    <lang:inline-script>

    package com.baeldung.springgroovyconfig;
    import com.baeldung.springgroovyconfig.NotificationService;

    class Notifier implements NotificationService {
        String message
    }

    </lang:inline-script>
    <lang:property name="message" value="Have a nice day!" />
</lang:groovy>

5.2、使用 GroovyScriptEngine {#52使用-groovyscriptengine}

还可以使用 GroovyScriptEngine 类来执行 Groovy 脚本。GroovyScriptEngine 由 Groovy 本身提供,使用它无需依赖 Spring。

该类支持在脚本发生变化时自动重新加载脚本。此外,它还会加载所有依赖于它的类。

执行脚本有两种方法。一种是获取一个 GroovyObject,然后通过调用 invokeMethod() 来执行脚本:

GroovyScriptEngine engine = new GroovyScriptEngine(ResourceUtils.getFile("file:src/main/resources/")
.getAbsolutePath(), this.getClass().getClassLoader());
Class<GroovyObject> joinerClass = engine.loadScriptByName("StringJoiner.groovy");
GroovyObject joiner = joinerClass.newInstance();

Object result = joiner.invokeMethod("join", new Object[]{"Mr.", "Bob"});
assertEquals("Mr.Bob", result.toString());

另一种是直接调用 Groovy 脚本。使用 Binding 类将变量传递给 Groovy 脚本:

Binding binding = new Binding();
binding.setVariable("arg1", "Mr.");
binding.setVariable("arg2", "Bob");
Object result = engine.run("StringJoinerScript.groovy", binding); 
assertEquals("Mr.Bob", result.toString());

6、总结 {#6总结}

在本文中,我们了解了 Spring 如何为 Groovy 提供广泛支持,从而使可以使用不同的方法获得有效的 Bean 定义。此外,还了解了如何将 Groovy 脚本加载为有效的 Spring Bean。最后,学习了如何即时调用 Groovy 脚本。


参考:https://www.baeldung.com/groovy/spring-using-groovy

赞(1)
未经允许不得转载:工具盒子 » 在 Spring 中使用 Groovy