Scripting Support

Spring Integration 2.1 为 JSR223 Scripting for Java specification 添加了支持,该支持是在 Java 版本 6 中引入的。它允许您使用用任何受支持语言(包括 Ruby、JRuby、Groovy 和 Kotlin)编写的脚本来提供各种集成组件的逻辑,类似于在 Spring Integration 中使用 Spring 表达式语言 (SpEL) 的方式。有关 JSR223 的更多信息,请参阅 documentation。 你需要将此依赖项包含在你的项目中:

  • Maven

  • Gradle

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

此外,你还需要添加一个脚本引擎实现,例如 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 Integration 的兼容性取决于其与规范的符合度以及实施者对规范的解释。

如果您计划使用 Groovy 作为脚本语言,我们建议您使用 Spring-Integration’s Groovy Support,因为它提供了特定于 Groovy 的其他功能。然而,本节也相关。

Script Configuration

根据你的集成要求的复杂性,可以在 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"/>

内联脚本不能重新加载。

Script Variable Bindings

变量绑定是让脚本可以引用外部提供给脚本执行上下文的变量的必要条件。默认情况下,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 的引用。在使用属性占位符配置或命令行参数时,这可能很有用。

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

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> 元素与消息中的 payloadheaders 变量合并。

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

GraalVM Polyglot

从版本 6.0 开始,该框架提供了一个 PolyglotScriptExecutor,它基于 GraalVM Polyglot API。由 Java 本身移除的 JavaScript 的 JSR223 引擎实现已被使用这个新的脚本执行器取代。请参阅有关在 GraalVM 中启用 JavaScript 支持的更多信息以及哪些 configuration options 可以通过脚本变量传播。默认情况下,该框架在共享的多态 Context 上设置 allowAllAccesstrue,从而启用与主机 JVM 的此交互:

  • 创建和使用新线程。

  • 对公共主机类的访问。

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

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

  • 对主机系统进行不受限制的 IO 操作。

  • Passing experimental options.

  • 创建和使用新子进程。

  • 访问进程环境变量。

可以通过接受 org.graalvm.polyglot.Context.Builder 的重载 PolyglotScriptExecutor 构造函数来自定义此功能。

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