脚本支持

Spring Integration 2.1 添加了对 Java 6 版中引入的 JSR223 Java 脚本规范 的支持。它允许您使用任何受支持语言(包括 Ruby、JRuby、Groovy 和 Kotlin)编写的脚本为各种集成组件提供逻辑,类似于在 Spring 集成中使用 Spring 表达式语言 (SpEL) 的方式。有关 JSR223 的更多信息,请参阅 文档

您需要将此依赖项包含到您的项目中

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-scripting</artifactId>
    <version>6.3.5</version>
</dependency>
compile "org.springframework.integration:spring-integration-scripting:6.3.5"

此外,您需要添加一个脚本引擎实现,例如 JRuby、Jython。

从 5.2 版本开始,Spring Integration 提供了 Kotlin Jsr223 支持。您需要将此依赖项添加到您的项目中才能使其正常工作

  • Maven

  • Gradle

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-scripting-jsr223</artifactId>
    <scope>runtime</scope>
</dependency>
runtime 'org.jetbrains.kotlin:kotlin-scripting-jsr223'

为了使用 JVM 脚本语言,必须在类路径中包含该语言的 JSR223 实现。GroovyJRuby 项目在其标准发行版中提供 JSR233 支持。

各种 JSR223 语言实现已由第三方开发。特定实现与 Spring 集成的兼容性取决于其符合规范的程度以及实现者对规范的解释。
如果您计划使用 Groovy 作为您的脚本语言,我们建议您使用 Spring 集成的 Groovy 支持,因为它提供了特定于 Groovy 的附加功能。但是,本节内容也相关。

脚本配置

根据您集成需求的复杂性,脚本可以作为 XML 配置中的 CDATA 内联提供,也可以作为包含脚本的 Spring 资源的引用提供。为了启用脚本支持,Spring Integration 定义了一个 ScriptExecutingMessageProcessor,它将消息有效负载绑定到名为 payload 的变量,并将消息头绑定到 headers 变量,两者都可以在脚本执行上下文中访问。您只需编写一个使用这些变量的脚本即可。以下示例对显示创建过滤器的样本配置

  • Java DSL

  • XML

@Bean
public IntegrationFlow scriptFilter() {
    return f -> f.filter(Scripts.processor("some/path/to/ruby/script/RubyFilterTests.rb"));
}
...
@Bean
public Resource scriptResource() {
	return new ByteArrayResource("headers.type == 'good'".getBytes());
}

@Bean
public IntegrationFlow scriptFilter() {
	return f -> f.filter(Scripts.processor(scriptResource()).lang("groovy"));
}
<int:filter input-channel="referencedScriptInput">
   <int-script:script location="some/path/to/ruby/script/RubyFilterTests.rb"/>
</int:filter>

<int:filter input-channel="inlineScriptInput">
     <int-script:script lang="groovy">
     <![CDATA[
     return payload == 'good'
   ]]>
  </int-script:script>
</int:filter>

如前面的示例所示,脚本可以内联包含,也可以通过对资源位置的引用来包含(通过使用 location 属性)。此外,lang 属性对应于语言名称(或其 JSR223 别名)。

其他支持脚本的 Spring Integration 端点元素包括 routerservice-activatortransformersplitter。每种情况下,脚本配置都与上述配置相同(端点元素除外)。

脚本支持的另一个有用功能是能够更新(重新加载)脚本,而无需重新启动应用程序上下文。为此,请在 script 元素上指定 refresh-check-delay 属性,如下例所示

  • Java DSL

  • XML

Scripts.processor(...).refreshCheckDelay(5000)
}
<int-script:script location="..." refresh-check-delay="5000"/>

在前面的示例中,每 5 秒检查一次脚本位置的更新。如果脚本已更新,则在更新后 5 秒后发生的任何调用都会导致运行新脚本。

考虑以下示例

  • Java DSL

  • XML

Scripts.processor(...).refreshCheckDelay(0)
}
<int-script:script location="..." refresh-check-delay="0"/>

在前面的示例中,只要发生这样的修改,上下文就会使用任何脚本修改进行更新,从而提供了一种简单的“实时”配置机制。任何负值都意味着在应用程序上下文初始化后不会重新加载脚本。这是默认行为。以下示例显示一个永不更新的脚本

  • Java DSL

  • XML

Scripts.processor(...).refreshCheckDelay(-1)
}
<int-script:script location="..." refresh-check-delay="-1"/>
内联脚本无法重新加载。

脚本变量绑定

需要变量绑定才能使脚本能够引用外部提供给脚本执行上下文的变量。默认情况下,payloadheaders 用作绑定变量。您可以使用 <variable> 元素(或 ScriptSpec.variables() 选项)将其他变量绑定到脚本,如下例所示

  • Java DSL

  • XML

Scripts.processor("foo/bar/MyScript.py")
    .variables(Map.of("var1", "thing1", "var2", "thing2", "date", date))
}
<script:script lang="py" location="foo/bar/MyScript.py">
    <script:variable name="var1" value="thing1"/>
    <script:variable name="var2" value="thing2"/>
    <script:variable name="date" ref="date"/>
</script:script>

如前面的示例所示,您可以将脚本变量绑定到标量值或 Spring bean 引用。请注意,payloadheaders 仍然包含为绑定变量。

使用 Spring Integration 3.0,除了 variable 元素外,还引入了 variables 属性。此属性和 variable 元素不是互斥的,您可以在一个 script 组件中组合它们。但是,无论变量在何处定义,变量都必须唯一。此外,从 Spring Integration 3.0 开始,内联脚本也允许变量绑定,如下例所示

<service-activator input-channel="input">
    <script:script lang="ruby" variables="thing1=THING1, date-ref=dateBean">
        <script:variable name="thing2" ref="thing2Bean"/>
        <script:variable name="thing3" value="thing2"/>
        <![CDATA[
            payload.foo = thing1
            payload.date = date
            payload.bar = thing2
            payload.baz = thing3
            payload
        ]]>
    </script:script>
</service-activator>

前面的示例显示了内联脚本、variable 元素和 variables 属性的组合。variables 属性包含一个逗号分隔的值,其中每个段包含一个用 '=' 分隔的变量及其值的对。变量名称可以后缀为 -ref,如前面的 date-ref 变量所示。这意味着绑定变量具有名称 date,但其值是对应用程序上下文中的 dateBean bean 的引用。当使用属性占位符配置或命令行参数时,这可能很有用。

如果您需要更精细地控制变量的生成方式,您可以实现自己的 Java 类,该类使用 ScriptVariableGenerator 策略,该策略由以下接口定义

public interface ScriptVariableGenerator {

    Map<String, Object> generateScriptVariables(Message<?> message);

}

此接口要求您实现 generateScriptVariables(Message) 方法。消息参数允许您访问消息有效负载和标头中可用的任何数据,返回值是绑定变量的 Map。每次为消息执行脚本时都会调用此方法。以下示例显示如何提供 ScriptVariableGenerator 的实现并使用 script-variable-generator 属性引用它

  • Java DSL

  • XML

Scripts.processor("foo/bar/MyScript.groovy")
    .variableGenerator(new foo.bar.MyScriptVariableGenerator())
}
<int-script:script location="foo/bar/MyScript.groovy"
        script-variable-generator="variableGenerator"/>

<bean id="variableGenerator" class="foo.bar.MyScriptVariableGenerator"/>

如果未提供 script-variable-generator,则脚本组件使用 DefaultScriptVariableGenerator,它在其 generateScriptVariables(Message) 方法中将任何提供的 <variable> 元素与 Message 中的 payloadheaders 变量合并。

您不能同时提供 script-variable-generator 属性和 <variable> 元素。它们是互斥的。

GraalVM 多语言

从 6.0 版本开始,框架提供了一个基于 GraalVM 多语言 APIPolyglotScriptExecutor。Java 本身已删除的 JavaScript JSR223 引擎实现已通过使用此新的脚本执行器来替换。查看有关在 GraalVM 中启用 JavaScript 支持以及哪些 配置选项 可以通过脚本变量传播的更多信息。默认情况下,框架将共享的多语言 Context 上的 allowAllAccess 设置为 true,这使得与主机 JVM 的这种交互成为可能

  • 创建和使用新线程。

  • 访问公共主机类。

  • 通过向类路径添加条目来加载新的主机类。

  • 将新成员导出到多语言绑定。

  • 主机系统上的不受限制的 IO 操作。

  • 传递实验性选项。

  • 创建和使用新的子进程。

  • 访问进程环境变量。

这可以通过重载的 PolyglotScriptExecutor 构造函数进行自定义,该构造函数接受 org.graalvm.polyglot.Context.Builder

要启用此 JavaScript 支持,必须使用安装了 js 组件的 GraalVM,或者在使用常规 JVM 时,必须包含 org.graalvm.sdk:graal-sdkorg.graalvm.js:js 依赖项。