1. 概述

Groovy 是一款强大的动态 JVM 语言,拥有众多特性。在 Spring 中使用 Groovy 能显著提升应用的灵活性和代码可读性。Spring 从 4.0 版本开始就支持基于 Groovy 的配置。

本文将探讨在 Spring 中集成 Groovy 的多种方式。我们将:

  • 使用 Spring 提供的不同选项创建 Groovy Bean 定义
  • 通过 Groovy 脚本引导应用上下文
  • 使用 XML 和 GroovyScriptEngine 类直接执行 Groovy 脚本(无需编译)

2. Maven 依赖

首先在 pom.xml 中添加 Groovy 依赖:

<dependency>
    <groupId>org.apache.groovy</groupId>
    <artifactId>groovy</artifactId>
    <version>4.0.21</version>
</dependency>

接着添加 GMavenPlus 插件编译 Groovy 文件:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.gmavenplus</groupId>
            <artifactId>gmavenplus-plugin</artifactId>
            <version>3.0.2</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>

⚠️ 虽然 GMavenPlus 插件允许将 Groovy 文件放在 src/main/java 目录,但强烈建议使用专门的 src/main/groovy 目录,避免后期维护混乱。

3. Bean 定义

传统开发中通过 XML 声明 Bean,后来被 Java 注解取代。现在我们还可以使用 Groovy 脚本定义 Bean。

3.1. 使用 Groovy Bean Builder

Groovy Bean Builder 是 Java @Configuration 和 XML 配置的强大替代方案。看几个基础示例:

beans {
    
    // 使用构造参数声明简单 Bean
    company(Company, name: 'ABC Inc');

    // 更简洁的语法:beanName(type, 构造参数)
    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')
}

顶层的 beans 闭包会被 GroovyBeanDefinitionReader 解析为 DSL 语法。

3.2. 使用注解

Groovy 类也可以作为 Spring Bean,完全替代 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

虽然 Groovy Bean Builder 和注解更灵活,但仍可通过 XML 声明 Groovy 脚本中的 Bean。Groovy 是动态语言,Spring 提供了全面支持,**需在 XML 中使用特殊元素 <lang:groovy>**:

<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 脚本位置
    • file: 前缀 → 文件系统路径
    • classpath: 前缀 → 类路径资源
  • refresh-check-delay:脚本刷新间隔(毫秒),修改后自动重载

4. 引导应用上下文

Spring 需要知道如何加载 Groovy 配置文件。可通过 web.xml 或编程方式实现。

4.1. 在 web.xml 中添加 Groovy 配置

Spring 4.1 新增了通过 web.xml 加载 Groovy 配置的支持,借助 GroovyWebApplicationContext

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

默认加载 /WEB-INF/applicationContext.groovy,可通过 contextConfigLocation 覆盖路径。

4.2. 使用 GenericGroovyApplicationContext

Spring 提供 GenericGroovyApplicationContext 引导 Groovy Bean 定义,支持内联定义:

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

或从外部文件加载

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

加载方式与 XmlWebApplicationContext/ClassPathXmlApplicationContext 类似。无额外配置时更简洁:

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

额外优势GenericGroovyApplicationContext 兼容 XML Bean 定义文件,支持与 Groovy 配置无缝混用

5. 执行 Groovy 脚本

除了 Bean 定义,Spring 还支持直接执行 Groovy 脚本(无需编译),可作为独立 Bean 或嵌入其他 Bean。

5.1. 作为内联脚本

利用 Spring 动态语言支持,将 Groovy 源码直接嵌入 XML 配置:

<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

GroovyScriptEngine 是 Groovy 自带的类(不依赖 Spring),支持脚本修改后自动重载,并重新加载依赖类。

两种执行方式:

方式一:通过 GroovyObject 调用

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());

方式二:直接调用脚本

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

使用 Binding 类向脚本传递变量。

6. 总结

Spring 提供了丰富的 Groovy 集成方案。结合 Groovy 的动态特性和 Spring 的灵活性,能显著提升开发效率。本文我们学习了:

  • ✅ 多种 Groovy Bean 定义方式(Bean Builder/注解/XML)
  • ✅ 引导 Groovy 应用上下文的方法
  • ✅ 动态执行 Groovy 脚本的技巧

示例代码可在 GitHub 获取。


原始标题:Using Groovy in Spring | Baeldung