服務啟動器

服務啟動器是一種端點類型,用於將任何 Spring 管理的物件連接到輸入通道,使其可以扮演服務的角色。如果服務產生輸出,也可以連接到輸出通道。或者,產生輸出的服務可以位於處理管線或訊息流程的末端,在這種情況下,可以使用輸入訊息的 replyChannel 標頭。如果未定義輸出通道,這是預設行為。與此處描述的大多數設定選項一樣,相同的行為實際上適用於大多數其他元件。

服務啟動器本質上是一個通用端點,用於使用輸入訊息(酬載和標頭)呼叫某些物件上的方法。其內部邏輯基於 MessageHandler,它可以是特定用例的任何可能實作,例如 DefaultMessageSplitterAggregatingMessageHandlerSftpMessageHandlerJpaOutboundGateway 等。因此,本參考手冊中提及的任何輸出閘道和輸出通道配接器都應被視為此服務啟動器端點的特定擴充;它們最終都會呼叫某些物件的方法。

設定服務啟動器

使用 Java 和註解設定,只需使用 @ServiceActivator 註解標記相應的服務方法 - 框架會在從輸入通道取用訊息時呼叫它

public class SomeService {

    @ServiceActivator(inputChannel = "exampleChannel")
    public void exampleHandler(SomeData payload) {
        ...
    }

}

請參閱註解支援中的更多資訊。

對於 Java、Groovy 或 Kotlin DSL,IntegrationFlow.handle() 運算子代表服務啟動器

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("exampleChannel")
             .handle(someService, "exampleHandler")
             .get();
}
@Bean
fun someFlow() =
    integrationFlow("exampleChannel") {
        handle(someService, "exampleHandler")
    }
@Bean
someFlow() {
    integrationFlow 'exampleChannel',
            {
                handle someService, 'exampleHandler'
            }
}

請參閱各章節中關於 DSL 的更多資訊

若要在使用 XML 設定時建立服務啟動器,請使用具有 'input-channel' 和 'ref' 屬性的 'service-activator' 元素,如下列範例所示

<int:service-activator input-channel="exampleChannel" ref="exampleHandler"/>

先前的設定會從 exampleHandler 中選取所有符合下列訊息傳遞需求的方法

  • 使用 @ServiceActivator 註解

  • public

  • 如果 requiresReply == true,則不傳回 void

執行階段呼叫的目標方法是針對每個請求訊息,依其 payload 類型選取,或者如果目標類別上存在此類方法,則作為 Message<?> 類型的後備。

從版本 5.0 開始,可以使用 @org.springframework.integration.annotation.Default 標記一個服務方法,作為所有不符合情況的後備。當使用內容類型轉換時,這可能很有用,目標方法會在轉換後呼叫。

若要委派給任何物件的明確定義方法,您可以新增 method 屬性,如下列範例所示

<int:service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/>

在任何一種情況下,當服務方法傳回非空值時,端點會嘗試將回覆訊息傳送至適當的回覆通道。若要判斷回覆通道,它首先會檢查端點設定中是否提供了 output-channel,如下列範例所示

<int:service-activator input-channel="exampleChannel" output-channel="replyChannel"
                       ref="somePojo" method="someMethod"/>

如果方法傳回結果且未定義 output-channel,則框架會檢查請求訊息的 replyChannel 標頭值。如果該值可用,則會檢查其類型。如果它是 MessageChannel,則回覆訊息會傳送到該通道。如果它是 String,則端點會嘗試將通道名稱解析為通道實例。如果無法解析通道,則會擲回 DestinationResolutionException。如果可以解析,則訊息會傳送到那裡。如果請求訊息沒有 replyChannel 標頭,且 reply 物件是 Message,則會查詢其 replyChannel 標頭以取得目標目的地。這是 Spring Integration 中用於請求-回覆訊息傳遞的技術,也是傳回位址模式的範例。

如果您的方法傳回結果,且您想要捨棄它並結束流程,則應將 output-channel 設定為傳送至 NullChannel。為了方便起見,框架會使用名稱 nullChannel 註冊一個。請參閱特殊通道以取得更多資訊。

服務啟動器是不需要產生回覆訊息的元件之一。如果您的方法傳回 null 或具有 void 傳回類型,則服務啟動器會在方法調用後退出,而不會發出任何訊號。此行為可以由 AbstractReplyProducingMessageHandler.requiresReply 選項控制,該選項在使用 XML 命名空間設定時也會公開為 requires-reply。如果該標誌設定為 true 且方法傳回 null,則會擲回 ReplyRequiredException

服務方法中的引數可以是訊息或任意類型。如果是後者,則會假定它是訊息酬載,該酬載會從訊息中擷取並注入到服務方法中。我們通常建議使用此方法,因為它遵循並推廣在使用 Spring Integration 時的 POJO 模型。引數也可以具有 @Header@Headers 註解,如註解支援中所述。

服務方法不需要有任何引數,這表示您可以實作事件樣式的服務啟動器(您只關心服務方法的調用),而不用擔心訊息的內容。可以將其視為空 JMS 訊息。此類實作的範例用例是輸入通道上存放的訊息的簡單計數器或監視器。

從版本 4.1 開始,框架會正確地將訊息屬性 (payloadheaders) 轉換為 Java 8 Optional POJO 方法參數,如下列範例所示

public class MyBean {
    public String computeValue(Optional<String> payload,
               @Header(value="foo", required=false) String foo1,
               @Header(value="foo") Optional<String> foo2) {
        if (payload.isPresent()) {
            String value = payload.get();
            ...
        }
        else {
           ...
       }
    }

}

如果自訂服務啟動器處理器實作可以在其他 <service-activator> 定義中重複使用,我們通常建議使用 ref 屬性。但是,如果自訂服務啟動器處理器實作僅在單個 <service-activator> 定義中使用,您可以提供內部 bean 定義,如下列範例所示

<int:service-activator id="exampleServiceActivator" input-channel="inChannel"
            output-channel = "outChannel" method="someMethod">
    <beans:bean class="org.something.ExampleServiceActivator"/>
</int:service-activator>
在同一個 <service-activator> 設定中同時使用 ref 屬性和內部處理器定義是不允許的,因為它會建立不明確的條件,並導致擲回例外狀況。
如果 ref 屬性參照擴充 AbstractMessageProducingHandler 的 bean(例如框架本身提供的處理器),則會透過將輸出通道直接注入到處理器來最佳化設定。在這種情況下,每個 ref 都必須指向單獨的 bean 實例(或 prototype 範圍的 bean)或使用內部 <bean/> 設定類型。如果您不小心從多個 bean 參照相同的訊息處理器,則會收到設定例外狀況。

服務啟動器和 Spring 運算式語言 (SpEL)

自 Spring Integration 2.0 起,服務啟動器也可以從SpEL中受益。

例如,您可以調用任何 bean 方法,而無需在 ref 屬性中指向 bean 或將其包含為內部 bean 定義,如下所示

<int:service-activator input-channel="in" output-channel="out"
	expression="@accountService.processAccount(payload, headers.accountId)"/>

	<bean id="accountService" class="thing1.thing2.Account"/>

在先前的設定中,我們未使用 ref 或作為內部 bean 注入 'accountService',而是使用 SpEL 的 @beanId 表示法,並調用採用與訊息酬載相容的類型的方法。我們也傳遞標頭值。任何有效的 SpEL 運算式都可以針對訊息中的任何內容進行評估。對於簡單的案例,如果所有邏輯都可以封裝在這樣的運算式中,您的服務啟動器不需要參照 bean,如下列範例所示

<int:service-activator input-channel="in" output-channel="out" expression="payload * 2"/>

在先前的設定中,我們的服務邏輯是將酬載值乘以 2。SpEL 讓我們可以相對輕鬆地處理它。

請參閱 Java DSL 章節中的服務啟動器和 .handle() 方法,以取得關於設定服務啟動器的更多資訊。

非同步服務啟動器

服務啟動器由呼叫執行緒調用。如果輸入通道是 SubscribableChannel,則這是上游執行緒,如果是 PollableChannel,則是輪詢器執行緒。如果服務傳回 CompletableFuture<?>,則預設動作是將其作為訊息的酬載傳送至輸出(或回覆)通道。從版本 4.3 開始,您現在可以將 async 屬性設定為 true(在使用 Java 設定時使用 setAsync(true))。如果服務在將 async 屬性設定為 true 時傳回 CompletableFuture<?>,則呼叫執行緒會立即釋放,並且回覆訊息會在完成 future 的執行緒(從您的服務內部)上傳送。對於使用 PollableChannel 的長時間執行服務而言,這尤其有利,因為輪詢器執行緒會釋放以執行框架內的其他服務。

如果服務使用 Exception 完成 future,則會發生正常的錯誤處理。ErrorMessage 會傳送到 errorChannel 訊息標頭(如果存在)。否則,ErrorMessage 會傳送到預設的 errorChannel(如果可用)。

從版本 6.1 開始,如果 AbstractMessageProducingHandler 的輸出通道設定為 ReactiveStreamsSubscribableChannel,則預設會開啟非同步模式。如果處理器結果不是反應式類型或 CompletableFuture<?>,則無論輸出通道類型為何,都會發生常規的回覆產生程序。

另請參閱反應式串流支援以取得更多資訊。

服務啟動器和方法傳回類型

服務方法可以傳回任何類型,該類型會變成回覆訊息酬載。在這種情況下,會建立新的 Message<?> 物件,並複製請求訊息中的所有標頭。當互動基於 POJO 方法調用時,這對於大多數 Spring Integration MessageHandler 實作都以相同的方式運作。

也可以從方法傳回完整的 Message<?> 物件。但是,請記住,與轉換器不同,對於服務啟動器,如果傳回的訊息中尚未存在標頭,則會透過從請求訊息複製標頭來修改此訊息。因此,如果您的方法參數是 Message<?>,並且您在服務方法中複製了一些但並非所有現有的標頭,則它們將在回覆訊息中重新出現。服務啟動器不負責從回覆訊息中移除標頭,並且為了遵循鬆耦合原則,最好在整合流程中新增 HeaderFilter。或者,可以使用轉換器來代替服務啟動器,但在這種情況下,當傳回完整的 Message<?> 時,方法完全負責訊息,包括複製請求訊息標頭(如果需要)。您必須確保重要的框架標頭(例如 replyChannelerrorChannel)(如果存在)必須保留。