Scripting Support
Spring Integration 2.1 为 JSR223 Scripting for Java specification 添加了支持,该支持是在 Java 版本 6 中引入的。它允许您使用用任何受支持语言(包括 Ruby、JRuby、Groovy 和 Kotlin)编写的脚本来提供各种集成组件的逻辑,类似于在 Spring Integration 中使用 Spring 表达式语言 (SpEL) 的方式。有关 JSR223 的更多信息,请参阅 documentation。
Spring Integration 2.1 added support for the JSR223 Scripting for Java specification, introduced in Java version 6. It lets you use scripts written in any supported language (including Ruby, JRuby, Groovy and Kotlin) to provide the logic for various integration components, similar to the way the Spring Expression Language (SpEL) is used in Spring Integration. For more information about JSR223, see the documentation.
你需要将此依赖项包含在你的项目中:
You need to include this dependency into your project:
-
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。
In addition, you need to add a script engine implementation, e.g. JRuby, Jython.
从版本 5.2 开始,Spring Integration 提供了 Kotlin Jsr223 支持。你需要将此依赖项添加到你的项目中以使其正常工作:
Starting with version 5.2, Spring Integration provides a Kotlin Jsr223 support. You need to add this dependency into your project to make it working:
-
Maven
-
Gradle
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
<scope>runtime</scope>
</dependency>
runtime 'org.jetbrains.kotlin:kotlin-scripting-jsr223'
In order to use a JVM scripting language, a JSR223 implementation for that language must be included in your class path. The Groovy and JRuby projects provide JSR233 support in their standard distributions.
第三方已开发出各种 JSR223 语言实现。特定实现与 Spring Integration 的兼容性取决于其与规范的符合度以及实施者对规范的解释。
Various JSR223 language implementations have been developed by third parties. A particular implementation’s compatibility with Spring Integration depends on how well it conforms to the specification and the implementer’s interpretation of the specification.
如果您计划使用 Groovy 作为脚本语言,我们建议您使用 Spring-Integration’s Groovy Support,因为它提供了特定于 Groovy 的其他功能。然而,本节也相关。 |
If you plan to use Groovy as your scripting language, we recommended you use Spring-Integration’s Groovy Support as it offers additional features specific to Groovy. However, this section is relevant as well. |
Script Configuration
根据你的集成要求的复杂性,可以在 XML 配置中作为 CDATA 以内联方式提供脚本,或作为包含脚本的 Spring 资源的引用提供脚本。为了启用脚本支持,Spring Integration 定义了一个 ScriptExecutingMessageProcessor
,它将消息负载绑定到名为 payload
的变量,并将消息头绑定到 headers
变量,在脚本执行上下文中都可以访问这两个变量。你所需要做的就是编写一个使用这些变量的脚本。以下两个示例展示了创建筛选器的示例配置:
Depending on the complexity of your integration requirements, scripts may be provided inline as CDATA in XML configuration or as a reference to a Spring resource that contains the script.
To enable scripting support, Spring Integration defines a ScriptExecutingMessageProcessor
, which binds the message payload to a variable named payload
and the message headers to a headers
variable, both accessible within the script execution context.
All you need to do is write a script that uses these variables.
The following pair of examples show sample configurations that create filters:
-
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 别名)。
As the preceding examples show, the script can be included inline or can be included by reference to a resource location (by using the location
attribute).
Additionally, the lang
attribute corresponds to the language name (or its JSR223 alias).
支持脚本的其他 Spring Integration 端点元素包括 router
、service-activator
、transformer
和 splitter
。在每种情况下,脚本配置都与上述配置相同(端点元素除外)。
Other Spring Integration endpoint elements that support scripting include router
, service-activator
, transformer
, and splitter
.
The scripting configuration in each case would be identical to the above (besides the endpoint element).
脚本支持的另一个有用功能是能够更新 (重新加载) 脚本而无需重新启动应用程序上下文。为此,请指定 script
元素上的 refresh-check-delay
属性,如下面的示例所示:
Another useful feature of scripting support is the ability to update (reload) scripts without having to restart the application context.
To do so, specify the refresh-check-delay
attribute on the script
element, as the following example shows:
-
Java DSL
-
XML
Scripts.processor(...).refreshCheckDelay(5000)
}
<int-script:script location="..." refresh-check-delay="5000"/>
上述示例中,每隔5秒将检查脚本位置是否更新。如果脚本已更新,则自更新以来5秒钟后发生的任何调用都会运行新脚本。
In the preceding example, the script location is checked for updates every 5 seconds. If the script is updated, any invocation that occurs later than 5 seconds since the update results in running the new script.
请考虑以下示例:
Consider the following example:
-
Java DSL
-
XML
Scripts.processor(...).refreshCheckDelay(0)
}
<int-script:script location="..." refresh-check-delay="0"/>
上述示例中,只要出现脚本改动,上下文就会使用任何脚本改动进行更新,从而提供了一种简单机制实现“实时”配置。任何负值意味着应用程序上下文初始化后不重新加载脚本。这是默认行为。下面示例显示了一个从未更新的脚本:
In the preceding example, the context is updated with any script modifications as soon as such modification occurs, providing a simple mechanism for 'real-time' configuration. Any negative value means the script is not reloaded after initialization of the application context. This is the default behavior. The following example shows a script that never updates:
-
Java DSL
-
XML
Scripts.processor(...).refreshCheckDelay(-1)
}
<int-script:script location="..." refresh-check-delay="-1"/>
内联脚本不能重新加载。
Inline scripts can not be reloaded.
Script Variable Bindings
变量绑定是让脚本可以引用外部提供给脚本执行上下文的变量的必要条件。默认情况下,payload
和 headers
用作绑定变量。您可以使用 <variable>
元素(或 ScriptSpec.variables()
选项),将其他变量绑定到脚本,如下面示例所示:
Variable bindings are required to enable the script to reference variables externally provided to the script’s execution context.
By default, payload
and headers
are used as binding variables.
You can bind additional variables to a script by using <variable>
elements (or ScriptSpec.variables()
option), as the following example shows:
-
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 引用。请注意,payload
和 headers
仍然包括在绑定变量中。
As shown in the preceding example, you can bind a script variable either to a scalar value or to a Spring bean reference.
Note that payload
and headers
are still included as binding variables.
使用 Spring Integration 3.0,除了 variable
元素,还引入了 variables
属性。此属性和 variable
元素并非互相排斥的,您可以在一个 script
组件中将它们组合在一起。但是,变量必须是唯一的,无论它们定义在哪。此外,从 Spring Integration 3.0 开始,还允许行内脚本进行变量绑定,如下示例所示:
With Spring Integration 3.0, in addition to the variable
element, the variables
attribute has been introduced.
This attribute and the variable
elements are not mutually exclusive, and you can combine them within one script
component.
However, variables must be unique, regardless of where they are defined.
Also, since Spring Integration 3.0, variable bindings are allowed for inline scripts, too, as the following example shows:
<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 的引用。在使用属性占位符配置或命令行参数时,这可能很有用。
The preceding example shows a combination of an inline script, a variable
element, and a variables
attribute.
The variables
attribute contains a comma-separated value, where each segment contains an '=' separated pair of the variable and its value.
The variable name can be suffixed with -ref
, as in the date-ref
variable in the preceding example.
That means that the binding variable has the name, date
, but the value is a reference to the dateBean
bean from the application context.
This may be useful when using property placeholder configuration or command-line arguments.
如果您需要更多地控制变量生成方式,可以实现一个使用 ScriptVariableGenerator
策略的 Java 类,该策略由以下接口定义:
If you need more control over how variables are generated, you can implement your own Java class that uses the ScriptVariableGenerator
strategy, which is defined by the following interface:
public interface ScriptVariableGenerator {
Map<String, Object> generateScriptVariables(Message<?> message);
}
此接口要求您实现 generateScriptVariables(Message)
方法。消息参数允许您访问消息负载和标头中可用的任何数据,返回值是绑定变量的 Map
。每当对消息执行脚本时,都会调用此方法。以下示例演示了如何提供 ScriptVariableGenerator
的实现,并使用 script-variable-generator
属性引用它:
This interface requires you to implement the generateScriptVariables(Message)
method.
The message argument lets you access any data available in the message payload and headers, and the return value is the Map
of bound variables.
This method is called every time the script is executed for a message.
The following example shows how to provide an implementation of ScriptVariableGenerator
and reference it with the script-variable-generator
attribute:
-
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>
元素与消息中的 payload
和 headers
变量合并。
If a script-variable-generator
is not provided, script components use DefaultScriptVariableGenerator
, which merges any provided <variable>
elements with payload
and headers
variables from the Message
in its generateScriptVariables(Message)
method.
您不能同时提供`script-variable-generator`属性和`<variable>`元素。它们是互斥的。
You cannot provide both the script-variable-generator
attribute and <variable>
element(s).
They are mutually exclusive.
GraalVM Polyglot
从版本 6.0 开始,该框架提供了一个 PolyglotScriptExecutor
,它基于 GraalVM Polyglot API。由 Java 本身移除的 JavaScript 的 JSR223 引擎实现已被使用这个新的脚本执行器取代。请参阅有关在 GraalVM 中启用 JavaScript 支持的更多信息以及哪些 configuration options 可以通过脚本变量传播。默认情况下,该框架在共享的多态 Context
上设置 allowAllAccess
至 true
,从而启用与主机 JVM 的此交互:
Starting with version 6.0, the framework provides a PolyglotScriptExecutor
which is based the GraalVM Polyglot API.
The JSR223 engine implementation for JavaScript, removed from Java by itself, has been replaced by using this new script executor.
See more information about enabling JavaScript support in GraalVM and what configuration options can be propagated via script variables.
By default, the framework sets allowAllAccess
to true
on the shared Polyglot Context
which enables this interaction with host JVM:
-
The creation and use of new threads.
-
The access to public host classes.
-
The loading of new host classes by adding entries to the class path.
-
Exporting new members into the polyglot bindings.
-
Unrestricted IO operations on host system.
-
Passing experimental options.
-
The creation and use of new sub-processes.
-
The access to process environment variables.
可以通过接受 org.graalvm.polyglot.Context.Builder
的重载 PolyglotScriptExecutor
构造函数来自定义此功能。
This can be customized via overloaded PolyglotScriptExecutor
constructor which accepts a org.graalvm.polyglot.Context.Builder
.
要启用此 JavaScript 支持,必须使用安装了 js
组件的 GraalVM,或者在使用常规 JVM 时,必须包含 org.graalvm.sdk:graal-sdk
和 org.graalvm.js:js
依赖关系。
To enable this JavaScript support, GraalVM with the js
component installed has to be used or, when using a regular JVM, the org.graalvm.sdk:graal-sdk
and org.graalvm.js:js
dependencies must be included.