指令碼支援

Spring Integration 2.1 添加了對 JSR223 Java 指令碼規範的支援,該規範在 Java 6 版本中引入。它允許您使用任何支援的語言(包括 Ruby、JRuby、Groovy 和 Kotlin)編寫的指令碼來提供各種整合元件的邏輯,類似於 Spring Integration 中使用 Spring 表示式語言 (SpEL) 的方式。有關 JSR223 的更多資訊,請參閱文件

您需要在專案中包含此依賴項

  • Maven

  • Gradle

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

此外,您需要新增指令碼引擎實現,例如 JRuby。

從版本 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 的 Groovy 支援,因為它提供了特定於 Groovy 的附加功能。但是,本節也相關。

指令碼配置

根據您的整合需求的複雜性,指令碼可以作為 CDATA 內聯在 XML 配置中提供,或作為對包含指令碼的 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> 元素與來自 Messagepayloadheaders 變數。

您不能同時提供 script-variable-generator 屬性和 <variable> 元素。它們是互斥的。

GraalVM Polyglot

從版本 6.0 開始,框架提供了基於 GraalVM Polyglot APIPolyglotScriptExecutor。Java 本身已移除的 JavaScript JSR223 引擎實現已替換為使用此新的指令碼執行器。有關在 GraalVM 中啟用 JavaScript 支援以及哪些配置選項可以透過指令碼變數傳播的更多資訊。特別是,必須將 org.graalvm.polyglot:js 依賴項新增到目標專案以支援 JavaScript。

從版本 6.4 開始,Python 指令碼支援也已遷移到 GraalVM Polyglot。現在這些指令碼可以用 Python 3.x 編寫,並且可以使用第三方庫。更多資訊請參閱 GraalPy 文件。特別是,必須將 rg.graalvm.polyglot:python 依賴項新增到目標專案以支援 Python。

預設情況下,框架在共享的 Polyglot Context 上將 allowAllAccess 設定為 true,從而啟用與主機 JVM 的互動

  • 新執行緒的建立和使用。

  • 對公共主機類的訪問。

  • 透過向類路徑新增條目來載入新的主機類。

  • 將新成員匯出到 polyglot 繫結中。

  • 對主機系統進行無限制的 IO 操作。

  • 傳遞實驗性選項。

  • 新子程序的建立和使用。

  • 訪問程序環境變數。

這可以透過接受 org.graalvm.polyglot.Context.Builder 的過載 PolyglotScriptExecutor 建構函式進行自定義。例如,基於 Jython 的指令碼仍然可以使用 option("python.EmulateJython", "true") 執行。但是,建議完全遷移到 GraalPy 以獲得更好的直譯器效能。因此,Java 類的 import 不再有效,而是必須使用 import java 及其 java.type() 函式。