指令碼支援
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'
各種 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 端點元素包括 router
、service-activator
、transformer
和 splitter
。在每種情況下,指令碼設定都與上述相同(除了端點元素之外)。
指令碼支援的另一個實用功能是能夠更新(重新載入)指令碼,而無需重新啟動應用程式內容。為此,請在 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"/>
內嵌指令碼無法重新載入。 |
指令碼變數繫結
變數繫結是必要的,以使指令碼能夠參考外部提供給指令碼執行內容的變數。預設情況下,payload
和 headers
用作繫結變數。您可以使用 <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 參考。請注意,payload
和 headers
仍然包含為繫結變數。
在 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)
方法中 Message
的 payload
和 headers
變數合併。
您不能同時提供 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-sdk
和 org.graalvm.js:js
相依性。