サービスアクティベーター

サービスアクティベータは、Spring が管理するオブジェクトを入力チャネルに接続して、サービスのロールを果たすようにするためのエンドポイント型です。サービスが出力を生成する場合、出力チャネルにも接続される場合があります。または、出力生成サービスを処理パイプラインまたはメッセージフローの最後に配置することもできます。この場合、受信メッセージの replyChannel ヘッダーを使用できます。これは、出力チャネルが定義されていない場合のデフォルトの動作です。ここで説明するほとんどの構成オプションと同様に、実際には同じ動作が他のほとんどのコンポーネントに適用されます。

Service Activator の構成

サービスアクティベータを作成するには、次の例に示すように、"input-channel" および "ref" 属性を使用して 'service-activator' 要素を使用します。

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

前述の構成では、次のメッセージング要件のいずれかを満たす exampleHandler からすべてのメソッドを選択します。

  • @ServiceActivator のアノテーションが付けられています

  • public

  • requiresReply == true の場合、void を返さない

実行時の呼び出しのターゲットメソッドは、各リクエストメッセージに対して payload 型によって、またはそのようなメソッドがターゲットクラスに存在する場合は Message<?> 型へのフォールバックとして選択されます。

バージョン 5.0 以降、1 つのサービスメソッドを、一致しないすべてのケースのフォールバックとして @org.springframework.integration.annotation.Default でマークできます。これは、変換後に呼び出されるターゲットメソッドでコンテンツ型変換を使用する場合に役立ちます。

オブジェクトの明示的に定義されたメソッドに委譲するには、次の例に示すように、method 属性を追加できます。

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

いずれの場合でも、サービスメソッドが null 以外の値を返すと、エンドポイントは応答メッセージを適切な応答チャネルに送信しようとします。応答チャネルを決定するには、次の例に示すように、エンドポイント構成で 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 という名前で登録します。詳細については、特別チャンネルを参照してください。

サービスアクティベータは、応答メッセージを生成するために必要ではないコンポーネントの 1 つです。メソッドが null を返す場合、または void 戻り型を持っている場合、サービスアクティベータはメソッド呼び出しの後にシグナルなしで終了します。この動作は、AbstractReplyProducingMessageHandler.requiresReply オプションによって制御できます。このオプションは、XML 名前空間で構成するときに requires-reply として公開されます。フラグが true に設定され、メソッドが null を返す場合、ReplyRequiredException がスローされます。

サービスメソッドの引数は、メッセージまたは任意の型のいずれかです。後者の場合、メッセージから抽出され、サービスメソッドに注入されるメッセージペイロードであると想定されます。Spring Integration を使用する場合、POJO モデルをフォローし、促進するため、一般的にこのアプローチをお勧めします。アノテーションサポートに従って、引数には @Header または @Headers アノテーションが付いている場合もあります。

サービスメソッドに引数を指定する必要はありません。つまり、イベントスタイルのサービスアクティベーター(サービスメソッドの呼び出しだけが重要)を実装でき、メッセージの内容を心配する必要はありません。null JMS メッセージと考えてください。このような実装の使用例の例は、入力チャネルに置かれたメッセージの単純なカウンターまたはモニターです。

次の例に示すように、バージョン 4.1 以降、フレームワークはメッセージプロパティ(payload および headers)を 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 -scoped Bean)にするか、内部 <bean/> 構成型を使用する必要があります。誤って複数の Bean から同じメッセージハンドラーを参照すると、構成例外が発生します。
サービスアクティベーターと Spring 式言語 (SpEL)

Spring Integration 2.0 以降、サービスアクティベーターも SpEL の恩恵を受けることができます。

例: 次のように、ref 属性の Bean を指すことなく、または内部 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 のポーラースレッドの場合、これはアップストリームスレッドです。サービスが ListenableFuture<?> を返す場合、デフォルトのアクションは、それを出力(または応答)チャネルに送信されるメッセージのペイロードとして送信することです。バージョン 4.3 から、async 属性を true に設定できるようになりました(Java 構成の使用時に setAsync(true) を使用)。この async 属性が true に設定されているときにサービスが ListenableFuture<?> を返すと、呼び出し元のスレッドはすぐに解放され、応答メッセージが(サービス内から)スレッドで送信され、将来を完了します。これは、ポーラースレッドがフレームワーク内で他のサービスを実行するために解放されるため、PollableChannel を使用する長時間実行サービスに特に有利です。

サービスが Exception で将来を完了すると、通常のエラー処理が発生します。ErrorMessage は、存在する場合、errorChannel メッセージヘッダーに送信されます。そうでない場合、ErrorMessage はデフォルトの errorChannel に送信されます(使用可能な場合)。

サービスアクティベーターとメソッドの戻り値の型

サービスメソッドは、応答メッセージペイロードになる任意の型を返すことができます。この場合、新しい Message<?> オブジェクトが作成され、リクエストメッセージのすべてのヘッダーがコピーされます。対話が POJO メソッド呼び出しに基づいている場合、これはほとんどの Spring Integration MessageHandler 実装で同じように機能します。

完全な Message<?> オブジェクトをメソッドから返すこともできます。ただし、Transformer とは異なり、Service Activator の場合、このメッセージは、返されたメッセージにまだ存在しない場合は、リクエストメッセージからヘッダーをコピーすることによって変更されることに注意してください。メソッドパラメーターが Message<?> であり、サービスメソッドの既存のヘッダーのすべてではなく一部をコピーすると、それらは応答メッセージに再表示されます。応答メッセージからヘッダーを削除することは ServiceActivator の責任ではなく、疎結合の原則を追求するために、統合フローに HeaderFilter を追加することをお勧めします。または、Service Activator の代わりに Transformer を使用することもできますが、その場合、完全な Message<?> を返すとき、メソッドは、リクエストメッセージヘッダーのコピー(必要な場合)を含め、メッセージに対して完全に責任があります。重要なフレームワークヘッダー(replyChannelerrorChannel など)が存在する場合は、それを保持する必要があることを確認する必要があります。