指令碼支援

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.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 Integration 的相容性取決於它是否符合規範以及實作者對規範的解釋。
如果您計劃使用 Groovy 作為您的指令碼語言,我們建議您使用 Spring-Integration 的 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) 方法。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,它將任何提供的 <variable> 元素與來自其 generateScriptVariables(Message) 方法中 Messagepayloadheaders 變數合併。

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

GraalVM Polyglot

從 6.0 版開始,框架提供了一個 PolyglotScriptExecutor,它基於 GraalVM Polyglot API。Java 本身移除的 JavaScript 的 JSR223 引擎實作已被使用這個新的指令碼執行器取代。請參閱有關在 GraalVM 中啟用 JavaScript 支援以及哪些 設定選項 可以透過指令碼變數傳播的更多資訊。預設情況下,框架在共用的 Polyglot Context 上將 allowAllAccess 設定為 true,這啟用了與主機 JVM 的互動

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

  • 存取公用主機類別。

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

  • 將新成員匯出到多語言繫結中。

  • 對主機系統的無限制 IO 作業。

  • 傳遞實驗性選項。

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

  • 存取程序環境變數。

這可以透過接受 org.graalvm.polyglot.Context.Builder 的多載 PolyglotScriptExecutor 建構函式進行自訂。

為了啟用此 JavaScript 支援,必須使用安裝了 js 元件的 GraalVM,或者,當使用常規 JVM 時,必須包含 org.graalvm.sdk:graal-sdkorg.graalvm.js:js 相依性。