構成

Spring Integration は、多くの構成オプションを提供します。どのオプションを選択するかは、特定のニーズと、どのレベルで作業するかによって異なります。一般的な Spring フレームワークと同様に、さまざまな手法を組み合わせて、手元の問題に合わせて調整できます。例: 大部分の構成で XSD ベースの名前空間を選択し、それをアノテーションで構成する少数のオブジェクトと組み合わせることができます。可能な限り、2 つは一貫した命名を提供します。XSD スキーマで定義された XML 要素はアノテーションの名前と一致し、それらの XML 要素の属性はアノテーションプロパティの名前と一致します。API を直接使用することもできますが、ほとんどの開発者は、より高いレベルのオプションのいずれか、名前空間ベースの構成とアノテーション駆動型の構成の組み合わせを選択することが期待されます。

名前空間サポート

Spring Integration コンポーネントを、エンタープライズ統合の用語と概念に直接マップする XML 要素で構成できます。多くの場合、要素名はエンタープライズ統合パターン (英語) ブックの要素名と一致します。

Spring 構成ファイル内で Spring Integration のコア名前空間のサポートを有効にするには、最上位の 'beans' 要素に次の名前空間参照とスキーママッピングを追加します。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:int="http://www.springframework.org/schema/integration"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/integration
           https://www.springframework.org/schema/integration/spring-integration.xsd">

(Spring Integration に特有の行を強調しました)

"xmlns:" の後に任意の名前を選択できます。わかりやすくするために int (Integration の略)を使用していますが、別の略語を使用することもできます。一方、XML エディターまたは IDE サポートを使用している場合は、自動補完機能を使用できるため、わかりやすくするために長い名前を使用する必要があります。または、次の例に示すように、Spring Integration スキーマをプライマリ名前空間として使用する構成ファイルを作成できます。

<beans:beans xmlns="http://www.springframework.org/schema/integration"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:beans="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           https://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/integration
           https://www.springframework.org/schema/integration/spring-integration.xsd">

(Spring Integration に特有の行を強調しました)

この代替を使用する場合、Spring Integration 要素にプレフィックスは必要ありません。一方、同じ構成ファイル内で汎用 Spring Bean を定義する場合、Bean エレメントにはプレフィックス(<beans:bean …​/>)が必要です。一般に、構成ファイル自体を(責任またはアーキテクチャーレイヤーに基づいて)モジュール化することをお勧めします。これらのファイル内ではジェネリクス Bean はほとんど必要ないため、統合に焦点を当てた構成ファイルで後者のアプローチを使用するのが適切であることがわかります。このドキュメントでは、統合ネームスペースがプライマリであると想定しています。

Spring Integration は、他の多くの名前空間を提供します。実際、名前空間のサポートを提供する各アダプター型(JMS、ファイルなど)は、個別のスキーマ内でその要素を定義します。これらの要素を使用するには、xmlns エントリと対応する schemaLocation マッピングで必要な名前空間を追加します。例: 次のルート要素は、これらの名前空間宣言のいくつかを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-file="http://www.springframework.org/schema/integration/file"
  xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
  xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
  xmlns:int-rmi="http://www.springframework.org/schema/integration/rmi"
  xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/file
    https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
    http://www.springframework.org/schema/integration/jms
    https://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
    http://www.springframework.org/schema/integration/mail
    https://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
    http://www.springframework.org/schema/integration/rmi
    https://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
    http://www.springframework.org/schema/integration/ws
    https://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
 ...
</beans>

このリファレンスマニュアルでは、対応する章のさまざまな要素の具体例を示します。ここで認識すべき主なことは、各名前空間 URI とスキーマの場所の命名の一貫性です。

タスクスケジューラの構成

Spring Integration では、ApplicationContext はメッセージバスの中心的なロールを果たします。いくつかの構成オプションのみを考慮する必要があります。最初に、主要な TaskScheduler インスタンスを制御したい場合があります。そのためには、taskScheduler という名前の単一の Bean を提供します。これは、次のように定数としても定義されます。

IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME

デフォルトでは、Spring Integration は、Spring Framework リファレンスマニュアルのタスクの実行とスケジューリングセクションに従って、ThreadPoolTaskScheduler のインスタンスに依存しています。デフォルトの TaskScheduler は 10 個のスレッドのプールで自動的に起動しますが、グローバルプロパティを参照してください。代わりに独自の TaskScheduler インスタンスを提供する場合、"autoStartup" プロパティを false に設定するか、独自のプールサイズ値を提供できます。

ポーリングコンシューマーが構成で明示的なタスクエグゼキューター参照を提供すると、ハンドラーメソッドの呼び出しはメインスケジューラープールではなく、エグゼキューターのスレッドプール内で発生します。ただし、エンドポイントのポーラーにタスクエグゼキューターが提供されていない場合、メインスケジューラーのスレッドの 1 つによって呼び出されます。

ポーラースレッドで長時間実行されるタスクを実行しないでください。代わりにタスクエグゼキューターを使用してください。ポーリングエンドポイントが多数ある場合、プールサイズを大きくしない限り、スレッド不足が発生する可能性があります。また、ポーリングコンシューマーのデフォルト receiveTimeout は 1 秒です。この時間はポーラースレッドがブロックするため、このようなエンドポイントが多数存在する場合は、タスクエグゼキューターを使用して、飢 again を回避することをお勧めします。あるいは、receiveTimeout を減らすことができます。
入力チャネルがキューベース(つまり、ポーリング可能)チャネルの 1 つである場合、エンドポイントはポーリングコンシューマーです。イベント駆動型コンシューマーとは、キューではなくディスパッチャーを備えた入力チャネルを持つコンシューマーです(つまり、サブスクライブ可能です)。そのようなエンドポイントには、ハンドラーが直接呼び出されるため、ポーラー構成がありません。

JEE コンテナーで実行する場合、デフォルトの taskScheduler の代わりに、​ ここで説明するように Spring の TimerManagerTaskScheduler を使用する必要がある場合があります。これを行うには、次の例に示すように、環境に適した JNDI 名で Bean を定義します。

<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler">
    <property name="jndiName" value="tm/MyTimerManager" />
    <property name="resourceRef" value="true" />
</bean>
カスタム TaskScheduler がアプリケーションコンテキストで構成されている場合(上記の DefaultManagedTaskScheduler のように)、フレームワークによって提供される ErrorMessage`s sent to the error channel, as is done with the default `TaskScheduler Bean として例外を処理できるように、MessagePublishingErrorHandler (integrationMessagePublishingErrorHandler Bean)を提供することをお勧めします。

詳細については、エラー処理も参照してください。

グローバルプロパティ

特定のグローバルフレームワークプロパティは、クラスパスでプロパティファイルを提供することでオーバーライドできます。

デフォルトのプロパティは org.springframework.integration.context.IntegrationProperties クラスにあります。次のリストは、デフォルト値を示しています。

spring.integration.channels.autoCreate=true (1)
spring.integration.channels.maxUnicastSubscribers=0x7fffffff (2)
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff (3)
spring.integration.taskScheduler.poolSize=10 (4)
spring.integration.messagingTemplate.throwExceptionOnLateReply=false (5)
spring.integration.readOnly.headers= (6)
spring.integration.endpoints.noAutoStartup= (7)
spring.integration.channels.error.requireSubscribers=true (8)
spring.integration.channels.error.ignoreFailures=true (9)
1true の場合、input-channel インスタンスは、アプリケーションコンテキストで明示的に検出されない場合、DirectChannel インスタンスとして自動的に宣言されます。
2DirectChannel などで許可されるデフォルトのサブスクライバー数を設定します。誤って同じチャネルに複数のエンドポイントをサブスクライブすることを避けるために使用できます。max-subscribers 属性を設定することにより、個々のチャネルでこれをオーバーライドできます。
3 このプロパティは、たとえば PublishSubscribeChannel で許可されるデフォルトのサブスクライバー数を提供します。これは、同じチャネルに予期しない数のエンドポイントを誤ってサブスクライブすることを回避するために使用できます。max-subscribers 属性を設定することにより、個々のチャネルでこれをオーバーライドできます。
4 デフォルトの taskScheduler Bean で使用可能なスレッドの数。タスクスケジューラの構成を参照してください。
5true の場合、ゲートウェイが応答を予期していない場合、ゲートウェイ応答チャネルに到着したメッセージは例外をスローします(送信スレッドがタイムアウトしたか、すでに応答を受信したため)。
6 ヘッダーコピー操作中に Message インスタンスに入力しないメッセージヘッダー名のコンマ区切りリスト。このリストは DefaultMessageBuilderFactory Bean によって使用され、MessageBuilder (MessageBuilder ヘルパークラスを参照)を介してメッセージを作成するために使用される IntegrationMessageHeaderAccessor インスタンス(MessageHeaderAccessor API を参照)に伝搬されます。デフォルトでは、MessageHeaders.ID と MessageHeaders.TIMESTAMP のみがメッセージの構築中にコピーされません。バージョン 4.3.2 以降。
7 アプリケーションの起動時に自動的に起動しない AbstractEndpoint Bean 名パターンのコンマ区切りリスト(xxx*xxx*xxx または xxx*yyy)。これらのエンドポイントは、Bean 名によって Control Bus (制御バスを参照)、SmartLifecycleRoleController でのロール(エンドポイントのロールを参照)、Lifecycle Bean インジェクションによって後で手動で開始できます。auto-startup XML アノテーションまたは autoStartup アノテーション属性を指定するか、Bean 定義で AbstractEndpoint.setAutoStartup() を呼び出すことにより、このグローバルプロパティの効果を明示的にオーバーライドできます。バージョン 4.3.12 以降。
8 デフォルトのグローバル errorChannel を requireSubscribers オプションで構成する必要があることを示すブールフラグ。バージョン 5.4.3 以降。詳細については、エラー処理を参照してください。
9 デフォルトのグローバル errorChannel がディスパッチングエラーを無視し、メッセージを次のハンドラーに渡す必要があることを示すブールフラグ。バージョン 5.5 以降。

これらのプロパティは、/META-INF/spring.integration.properties ファイルをクラスパスに追加するか、org.springframework.integration.context.IntegrationProperties インスタンスの場合は IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME Bean を追加することでオーバーライドできます。すべてのプロパティを指定する必要はありません。オーバーライドするプロパティのみを指定してください。

バージョン 5.1 以降、DEBUG ロジックレベルが org.springframework.integration カテゴリに対してオンになっている場合、アプリケーションコンテキストの起動後に、マージされたすべてのグローバルプロパティがログに出力されます。出力は次のようになります。

Spring Integration global properties:

spring.integration.endpoints.noAutoStartup=fooService*
spring.integration.taskScheduler.poolSize=20
spring.integration.channels.maxUnicastSubscribers=0x7fffffff
spring.integration.channels.autoCreate=true
spring.integration.channels.maxBroadcastSubscribers=0x7fffffff
spring.integration.readOnly.headers=
spring.integration.messagingTemplate.throwExceptionOnLateReply=true

アノテーションサポート

メッセージエンドポイントを構成するための XML 名前空間のサポートに加えて、アノテーションも使用できます。まず、Spring Integration は、クラスレベルの @MessageEndpoint をステレオタイプアノテーションとして提供します。つまり、Spring の @Component アノテーションが付けられているため、Spring のコンポーネントスキャンによって Bean 定義として自動的に認識されます。

さらに重要なのは、さまざまなメソッドレベルのアノテーションです。それらは、アノテーション付きメソッドがメッセージを処理できることを示しています。次の例は、クラスレベルとメソッドレベルの両方のアノテーションを示しています。

@MessageEndpoint
public class FooService {

    @ServiceActivator
    public void processMessage(Message message) {
        ...
    }
}

メソッドがメッセージを「処理」することの正確な意味は、特定のアノテーションに依存します。Spring Integration で使用できるアノテーションは次のとおりです。

XML 構成をアノテーションと組み合わせて使用する場合、@MessageEndpoint アノテーションは必要ありません。<service-activator/> 要素の ref 属性から POJO 参照を設定する場合、メソッドレベルのアノテーションのみを提供できます。その場合、メソッドレベルの属性が <service-activator/> 要素に存在しない場合でも、アノテーションによってあいまいさが回避されます。

ほとんどの場合、アノテーション付きハンドラーメソッドは、パラメーターとして Message 型を必要としません。代わりに、次の例に示すように、メソッドパラメーター型はメッセージのペイロード型と一致できます。

public class ThingService {

    @ServiceActivator
    public void bar(Thing thing) {
        ...
    }

}

メソッドパラメーターを MessageHeaders の値からマップする必要がある場合、別のオプションはパラメーターレベルの @Header アノテーションを使用することです。一般に、Spring Integration アノテーションが付けられたメソッドは、Message 自体、メッセージペイロード、ヘッダー値(@Header を使用)をパラメーターとして受け入れることができます。実際、次の例に示すように、メソッドは組み合わせを受け入れることができます。

public class ThingService {

    @ServiceActivator
    public void otherThing(String payload, @Header("x") int valueX, @Header("y") int valueY) {
        ...
    }

}

次の例に示すように、@Headers アノテーションを使用して、すべてのメッセージヘッダーを Map として提供することもできます。

public class ThingService {

    @ServiceActivator
    public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}
アノテーションの値は SpEL 式(someHeader.toUpperCase() など)にすることもできます。これは、挿入する前にヘッダー値を操作する場合に便利です。また、オプションの required プロパティも提供します。このプロパティは、ヘッダー内で属性値を使用可能にする必要があるかどうかを指定します。required プロパティのデフォルト値は true です。

これらのアノテーションのいくつかについて、メッセージ処理メソッドが null 以外の値を返すと、エンドポイントは応答を送信しようとします。これは、このようなエンドポイントの出力チャネルが使用され(使用可能な場合)、REPLY_CHANNEL メッセージヘッダー値がフォールバックとして使用されるという点で、両方の構成オプション(名前空間とアノテーション)で一貫しています。

エンドポイントの出力チャネルと応答チャネルメッセージヘッダーの組み合わせにより、パイプラインアプローチが可能になります。複数のコンポーネントに出力チャネルがあり、最終コンポーネントにより応答メッセージを応答チャネルに転送できます(元のリクエストメッセージで指定されています)。つまり、最終的なコンポーネントは元の送信者から提供された情報に依存し、結果として任意の数のクライアントを動的にサポートできます。これは、返信先アドレス (英語) パターンの例です。

ここに示す例に加えて、これらのアノテーションは、次の例が示すように、inputChannel および outputChannel プロパティもサポートします。

@Service
public class ThingService {

    @ServiceActivator(inputChannel="input", outputChannel="output")
    public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}

これらのアノテーションの処理により、対応する XML コンポーネントと同じ Bean が作成されます。AbstractEndpoint インスタンスおよび MessageHandler インスタンス(または受信チャネルアダプターの MessageSource インスタンス)。@Bean メソッドのアノテーションを参照してください。Bean 名は次のパターンから生成されます: [componentName].[methodName].[decapitalizedAnnotationClassShortName]。上記の例では、Bean の名前は AbstractEndpoint の thingService.otherThing.serviceActivator であり、MessageHandler (MessageSource)Bean の .handler (.source)サフィックスが追加された同じ名前です。このような名前は、@EndpointId アノテーションとこれらのメッセージングアノテーションを併用してカスタマイズできます。MessageHandler インスタンス(MessageSource インスタンス)も、メッセージ履歴によって追跡することができます

バージョン 4.0 以降、すべてのメッセージングアノテーションは SmartLifecycle オプション(autoStartup および phase)を提供し、アプリケーションコンテキストの初期化でエンドポイントのライフサイクルを制御できるようにします。デフォルトでは、それぞれ true と 0 になります。エンドポイント( `start()` や stop() など)の状態を変更するには、BeanFactory (またはオートワイヤー)を使用してエンドポイント Bean への参照を取得し、メソッドを呼び出すことができます。または、コマンドメッセージを Control Bus に送信することもできます(制御バスを参照)。これらの目的のために、前の段落で前述した beanName を使用する必要があります。

上記のアノテーション(特定のチャネル Bean が構成されていない場合)の解析後に自動的に作成されたチャネル、および対応するコンシューマーエンドポイントは、コンテキスト初期化の終わり近くで Bean として宣言されます。これらの Bean は他のサービスでオートワイヤーできますが、通常は通常のオートワイヤー処理中に定義がまだ利用できないため、@Lazy アノテーションでマークを付ける必要があります。

@Autowired
@Lazy
@Qualifier("someChannel")
MessageChannel someChannel;
...

@Bean
Thing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannelAdapter someInboundAdapter) {
    ...
}

@Poller アノテーションの使用

Spring Integration 4.0 より前のメッセージングアノテーションでは、inputChannel が SubscribableChannel への参照である必要がありました。PollableChannel インスタンスの場合、<int:poller/> を構成し、複合エンドポイントを PollingConsumer にするために <int:bridge/> 要素が必要でした。次の例に示すように、バージョン 4.0 には @Poller アノテーションが導入され、メッセージングアノテーションで poller 属性を直接設定できるようになりました。

public class AnnotationService {

    @Transformer(inputChannel = "input", outputChannel = "output",
        poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}"))
    public String handle(String payload) {
        ...
    }
}

@Poller アノテーションは、単純な PollerMetadata オプションのみを提供します。プロパティプレースホルダーを使用して、@Poller アノテーションの属性(maxMessagesPerPollfixedDelayfixedRatecron)を構成できます。また、バージョン 5.1 以降、PollingConsumer 用の receiveTimeout オプションも提供されます。より多くのポーリングオプション(たとえば、transactionadvice-chainerror-handler など)を提供する必要がある場合は、PollerMetadata を汎用 Bean として構成し、その Bean 名を @Poller の value 属性として使用する必要があります。この場合、他の属性は許可されません(PollerMetadata Bean で指定する必要があります)。inputChannel が PollableChannel であり、@Poller が構成されていない場合、デフォルトの PollerMetadata が使用されることに注意してください(アプリケーションコンテキストに存在する場合)。@Configuration アノテーションを使用してデフォルトのポーラーを宣言するには、次の例のようなコードを使用します。

@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
    PollerMetadata pollerMetadata = new PollerMetadata();
    pollerMetadata.setTrigger(new PeriodicTrigger(10));
    return pollerMetadata;
}

次の例は、デフォルトのポーラーの使用方法を示しています。

public class AnnotationService {

    @Transformer(inputChannel = "aPollableChannel", outputChannel = "output")
    public String handle(String payload) {
        ...
    }
}

次の例は、名前付きポーラーの使用方法を示しています。

@Bean
public PollerMetadata myPoller() {
    PollerMetadata pollerMetadata = new PollerMetadata();
    pollerMetadata.setTrigger(new PeriodicTrigger(1000));
    return pollerMetadata;
}

次の例は、デフォルトのポーラーを使用するエンドポイントを示しています。

public class AnnotationService {

    @Transformer(inputChannel = "aPollableChannel", outputChannel = "output"
                           poller = @Poller("myPoller"))
    public String handle(String payload) {
         ...
    }
}

バージョン 4.3.3 以降、@Poller アノテーションには errorChannel 属性があり、基礎となる MessagePublishingErrorHandler を簡単に構成できます。この属性は、<poller> XML コンポーネントの error-channel と同じロールを果たします。詳細については、エンドポイント名前空間のサポートを参照してください。

メッセージングアノテーションの poller() 属性は、reactive() 属性と相互に排他的です。詳細については、次のセクションを参照してください。

@Reactive アノテーションの使用

ReactiveStreamsConsumer はバージョン 5.0 から存在しますが、エンドポイントの入力チャネルが FluxMessageChannel (または任意の org.reactivestreams.Publisher 実装)である場合にのみ適用されました。バージョン 5.3 以降、ターゲットメッセージハンドラーが入力チャネル型に関係なく ReactiveMessageHandler である場合、そのインスタンスもフレームワークによって作成されます。@Reactive サブアノテーション(上記の @Poller と同様)は、バージョン 5.5 以降のすべてのメッセージングアノテーションに導入されました。オプションの Function<? super Flux<Message<?>>, ? extends Publisher<Message<?>>> Bean 参照を受け入れ、入力チャネル型およびメッセージハンドラーとは関係なく、ターゲットエンドポイントを ReactiveStreamsConsumer インスタンスに変換します。この関数は、Flux.transform() オペレーターから使用され、入力チャネルからのリアクティブストリームソースにカスタマイズ(publishOn()doOnNext()log()retry() など)を適用します。

次の例は、最終的なサブスクライバーとプロデューサーに関係なく、公開スレッドを入力チャネルからその DirectChannel に変更する方法を示しています。

@Bean
public Function<Flux<?>, Flux<?>> publishOnCustomizer() {
    return flux -> flux.publishOn(Schedulers.parallel());
}

@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer"))
public void handleReactive(String payload) {
    ...
}

メッセージングアノテーションの reactive() 属性は、poller() 属性と相互に排他的です。詳細については、@Poller アノテーションの使用および Reactive Streams サポートを参照してください。

@InboundChannelAdapter アノテーションの使用

バージョン 4.0 では、@InboundChannelAdapter メソッドレベルのアノテーションが導入されました。アノテーション付きメソッドの MethodInvokingMessageSource に基づいて SourcePollingChannelAdapter 統合コンポーネントを生成します。このアノテーションは <int:inbound-channel-adapter> XML コンポーネントに類似しており、同じ制限があります。メソッドにはパラメーターを指定できません。戻り値の型は void であってはなりません。(とオプションの @Poller アノテーション、value (必要な MessageChannel Bean 名)と poller : これは、2 つの属性があり、前述の)。MessageHeaders を提供する必要がある場合は、Message<?> 戻り型を使用し、MessageBuilder を使用して Message<?> を作成します。MessageBuilder を使用すると、MessageHeaders を構成できます。次の例は、@InboundChannelAdapter アノテーションの使用方法を示しています。

@InboundChannelAdapter("counterChannel")
public Integer count() {
    return this.counter.incrementAndGet();
}

@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000"))
public String foo() {
    return "foo";
}

バージョン 4.3 では、value アノテーション属性の channel エイリアスが導入され、ソースコードの読みやすさが向上しました。また、ターゲット MessageChannel Bean は、初期化段階ではなく、最初の receive() 呼び出しで提供された名前(outputChannelName オプションで設定)によって SourcePollingChannelAdapter で解決されます。これにより、「遅延バインディング」ロジックが可能になります。コンシューマーの観点からのターゲット MessageChannel Bean が作成され、@InboundChannelAdapter 解析フェーズよりも少し遅れて登録されます。

最初の例では、デフォルトポーラーがアプリケーションコンテキストの他の場所で宣言されている必要があります。

@MessagingGateway アノテーションの使用

@MessagingGateway アノテーションを参照してください。

@IntegrationComponentScan アノテーションの使用

標準の Spring Framework @ComponentScan アノテーションは、ステレオタイプ @Component アノテーションのインターフェースをスキャンしません。この制限を克服し、@MessagingGateway (@MessagingGateway アノテーションを参照)の構成を可能にするために、@IntegrationComponentScan メカニズムを導入しました。このアノテーションは、@Configuration アノテーションとともに配置し、basePackages や basePackageClasses などのスキャンオプションを定義するようにカスタマイズする必要があります。この場合、@MessagingGateway のアノテーションが付けられたすべての検出されたインターフェースが解析され、GatewayProxyFactoryBean インスタンスとして登録されます。他のすべてのクラスベースのコンポーネントは、標準の @ComponentScan によって解析されます。

メッセージングメタアノテーション

バージョン 4.0 以降、すべてのメッセージングアノテーションをメタアノテーションとして構成でき、すべてのユーザー定義のメッセージングアノテーションは同じ属性を定義してデフォルト値をオーバーライドできます。さらに、次の例に示すように、メタアノテーションを階層的に構成できます。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ServiceActivator(inputChannel = "annInput", outputChannel = "annOutput")
public @interface MyServiceActivator {

    String[] adviceChain = { "annAdvice" };
}

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@MyServiceActivator
public @interface MyServiceActivator1 {

    String inputChannel();

    String outputChannel();
}
...

@MyServiceActivator1(inputChannel = "inputChannel", outputChannel = "outputChannel")
public Object service(Object payload) {
   ...
}

メタアノテーションを階層的に構成すると、ユーザーはさまざまな属性のデフォルトを設定でき、フレームワーク Java のユーザーアノテーションへの依存関係を分離して、ユーザークラスでの使用を回避できます。フレームワークが、フレームワークメタアノテーションを持つユーザーアノテーションを持つメソッドを見つけると、そのメソッドがフレームワークアノテーションで直接アノテーション付けされたかのように扱われます。

@Bean メソッドのアノテーション

バージョン 4.0 以降では、@Configuration クラスの @Bean メソッド定義にメッセージングアノテーションを構成して、メソッドではなく Bean に基づいてメッセージエンドポイントを生成できます。@Bean 定義が「すぐに使える」 MessageHandler インスタンス(AggregatingMessageHandlerDefaultMessageSplitter など)、Transformer インスタンス(JsonToObjectTransformerClaimCheckOutTransformer など)、MessageSource インスタンス(FileReadingMessageSourceRedisStoreMessageSource など)である場合に役立ちます。次の例は、@Bean アノテーションでメッセージングアノテーションを使用する方法を示しています。

@Configuration
@EnableIntegration
public class MyFlowConfiguration {

    @Bean
    @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
    public MessageSource<String> consoleSource() {
        return CharacterStreamReadingMessageSource.stdin();
    }

    @Bean
    @Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel")
    public ObjectToMapTransformer toMapTransformer() {
        return new ObjectToMapTransformer();
    }

    @Bean
    @ServiceActivator(inputChannel = "httpChannel")
    public MessageHandler httpHandler() {
    HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://foo/service");
        handler.setExpectedResponseType(String.class);
        handler.setOutputChannelName("outputChannel");
        return handler;
    }

    @Bean
    @ServiceActivator(inputChannel = "outputChannel")
    public LoggingHandler loggingHandler() {
        return new LoggingHandler("info");
    }

}

バージョン 5.0 では、java.util.function.Supplier を返す @InboundChannelAdapter でアノテーションが付けられた @Bean のサポートが導入されました。これにより、POJO または Message のいずれかを生成できます。次の例は、その組み合わせの使用方法を示しています。

@Configuration
@EnableIntegration
public class MyFlowConfiguration {

    @Bean
    @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
    public Supplier<String> pojoSupplier() {
        return () -> "foo";
    }

    @Bean
    @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
    public Supplier<Message<String>> messageSupplier() {
        return () -> new GenericMessage<>("foo");
    }
}

メタアノテーションルールは @Bean メソッドでも機能します(前述の @MyServiceActivator アノテーションは @Bean 定義に適用できます)。

コンシューマー @Bean 定義でこれらのアノテーションを使用する場合、Bean 定義が適切な MessageHandler を返す場合(アノテーション型に応じて)、MessageHandler@Bean 定義自体に属性(outputChannelrequiresReplyorder など)を設定する必要があります。次のアノテーション属性のみが使用されます: adviceChainautoStartupinputChannelphasepoller。他のすべての属性はハンドラー用です。
Bean 名は、次のアルゴリズムで生成されます。
  • MessageHandler (MessageSource) @Bean は、@Bean のメソッド名または name 属性から独自の標準名を取得します。これは、@Bean メソッドにメッセージングアノテーションがなかったかのように機能します。

  • AbstractEndpoint Bean 名は、[configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName] というパターンで生成されます。例: 前に示したconsoleSource() 定義の SourcePollingChannelAdapter エンドポイントは、myFlowConfiguration.consoleSource.inboundChannelAdapter という Bean 名を取得します。エンドポイント Bean 名も参照してください。

@Bean 定義でこれらのアノテーションを使用する場合、inputChannel は宣言された Bean を参照する必要があります。この場合、チャネルは自動的に宣言されません。

Java 構成では、@Bean メソッドレベルで @Conditional (たとえば @Profile)定義を使用して、何らかの条件付きの理由で Bean 登録をスキップできます。次の例は、その方法を示しています。

@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("thing")
public MessageHandler skipped() {
    return System.out::println;
}

既存の Spring コンテナーロジックと一緒に、メッセージングエンドポイント Bean(@ServiceActivator アノテーションに基づく)も登録されていません。

アノテーション付きのブリッジを作成する

バージョン 4.0 以降、Java 構成は @BridgeFrom および @BridgeTo@Bean メソッドのアノテーションを提供して、@Configuration クラスの MessageChannel Bean をマークします。これらは完全を期すために実際に存在し、BridgeHandler とそのメッセージエンドポイント構成を宣言するための便利なメカニズムを提供します。

@Bean
public PollableChannel bridgeFromInput() {
    return new QueueChannel();
}

@Bean
@BridgeFrom(value = "bridgeFromInput", poller = @Poller(fixedDelay = "1000"))
public MessageChannel bridgeFromOutput() {
    return new DirectChannel();
}
@Bean
public QueueChannel bridgeToOutput() {
    return new QueueChannel();
}

@Bean
@BridgeTo("bridgeToOutput")
public MessageChannel bridgeToInput() {
    return new DirectChannel();
}

これらのアノテーションは、メタアノテーションとしても使用できます。

アノテーション付きエンドポイントへのアドバイス

メッセージマッピングのルールと規則

Spring Integration は、いくつかのデフォルトルールに依存して特定の規則を定義することにより、追加の構成を提供することなく、メッセージをメソッドとその引数にマッピングする柔軟な機能を実装しています。次のセクションの例は、ルールを明確にします。

サンプルシナリオ

次の例は、Map または非 void 戻り型の Properties オブジェクトではない単一のアノテーションなしパラメーター(オブジェクトまたはプリミティブ)を示しています。

public String doSomething(Object o);

入力パラメーターはメッセージのペイロードです。パラメーター型がメッセージペイロードと互換性がない場合は、Spring 3.0 が提供する変換サービスを使用してそれを変換しようとします。戻り値は、返されたメッセージのペイロードとして組み込まれます。

次の例は、Map または Message 戻り値の型の Properties ではない単一のアノテーションなしパラメーター(オブジェクトまたはプリミティブ)を示しています。

public Message doSomething(Object o);

入力パラメーターはメッセージのペイロードです。パラメーター型がメッセージペイロードと互換性がない場合は、Spring 3.0 が提供する変換サービスを使用してそれを変換しようとします。戻り値は、次の宛先に送信される新しく作成されたメッセージです。

次の例は、任意のオブジェクトまたはプリミティブな戻り型を持つメッセージ(またはそのサブクラスの 1 つ)である単一のパラメーターを示しています。

public int doSomething(Message  msg);

入力パラメーター自体は Message です。戻り値は、次の宛先に送信される Message のペイロードになります。

次の例は、Message (またはそのサブクラスの 1 つ)を戻り型として持つ Message (またはそのサブクラスの 1 つ)である単一のパラメーターを示しています。

public Message doSomething(Message msg);

入力パラメーター自体は Message です。戻り値は、新しく構築された Message であり、次の宛先に送信されます。

次の例は、Map または Properties の単一のパラメーターと戻り値の型として Message を示しています。

public Message doSomething(Map m);

これは少し興味深いです。最初は、メッセージヘッダーに直接簡単にマッピングできるように思われますが、常に Message ペイロードが優先されます。つまり、Message ペイロードの型が Map である場合、この入力引数は Message ペイロードを表します。ただし、Message ペイロードの型が Map でない場合、変換サービスはペイロードの変換を試行せず、入力引数はメッセージヘッダーにマップされます。

次の例は 2 つのパラメーターを示しています。1 つは Map または Properties オブジェクトではない任意の型(オブジェクトまたはプリミティブ)であり、もう 1 つは型 Map または Properties 型(戻り値に関係なく)です。

public Message doSomething(Map h, <T> t);

この組み合わせには、そのうちの 1 つが Map 型である 2 つの入力パラメーターが含まれます。Map 以外のパラメーター(順序に関係なく)は Message ペイロードにマッピングされ、Map または Properties (順序に関係なく)はメッセージヘッダーにマッピングされ、Message 構造と対話するための素晴らしい POJO 方法を提供します。

次の例は、パラメーターを示していません(戻り値に関係なく)。

public String doSomething();

このメッセージハンドラーメソッドは、このハンドラーが接続されている入力チャネルに送信されたメッセージに基づいて呼び出されます。ただし、Message データはマップされないため、Message はイベントまたはトリガーとして機能し、ハンドラーを呼び出します。出力は、前述の規則に従ってマッピングされます。

次の例は、パラメーターと void 戻り値を示しています。

public void soSomething();

この例は前の例と同じですが、出力は生成されません。

アノテーションベースのマッピング

アノテーションベースのマッピングは、メッセージをメソッドにマッピングするための最も安全で曖昧さの少ないアプローチです。次の例は、メソッドをヘッダーに明示的にマップする方法を示しています。

public String doSomething(@Payload String s, @Header("someheader") String b)

後で見ることができるように、アノテーションがない場合、このシグネチャーはあいまいな状態になります。ただし、最初の引数を Message ペイロードに明示的にマッピングし、2 番目の引数を someheader メッセージヘッダーの値に明示的にマッピングすることにより、あいまいさを回避します。

次の例は、前の例とほぼ同じです。

public String doSomething(@Payload String s, @RequestParam("something") String b)

@RequestMapping またはその他の Spring Integration 以外のマッピングアノテーションは無関係であるため無視され、2 番目のパラメーターはマッピングされません。2 番目のパラメーターは簡単にペイロードにマッピングできますが、ペイロードは 1 つしか存在できません。アノテーションにより、このメソッドがあいまいになりません。

次の例は、アノテーションが意図を明確にしないとあいまいになる別の同様の方法を示しています。

public String foo(String s, @Header("foo") String b)

唯一の違いは、最初の引数が暗黙的にメッセージペイロードにマップされることです。

次の例は、3 つ以上の引数があるため、アノテーションなしでは間違いなく処理される別の署名を示しています。

public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar)

引数の 2 つが Map インスタンスであるため、この例は特に問題になります。ただし、アノテーションベースのマッピングを使用すると、あいまいさを簡単に回避できます。この例では、最初の引数はすべてのメッセージヘッダーにマッピングされ、2 番目と 3 番目の引数は "something" および "someotherthing" という名前のメッセージヘッダーの値にマッピングされます。ペイロードはどの引数にもマップされていません。

複雑なシナリオ

次の例では、複数のパラメーターを使用しています。

複数のパラメーターは、適切なマッピングの決定に関して多くのあいまいさを生み出す可能性があります。一般的なアドバイスは、メソッドパラメーターに @Payload@Header@Headers アノテーションを付けることです。このセクションの例は、例外が発生する結果となるあいまいな条件を示しています。

public String doSomething(String s, int i)

2 つのパラメーターの重量は同じです。どれがペイロードであるかを判別する方法はありません。

次の例は、3 つのパラメーターのみがある同様の問題を示しています。

public String foo(String s, Map m, String b)

Map はメッセージヘッダーに簡単にマッピングできますが、2 つの String パラメーターをどうするかを決定する方法はありません。

次の例は、別のあいまいな方法を示しています。

public String foo(Map m, Map f)

1 つの Map をメッセージペイロードに、もう 1 つをメッセージヘッダーにマッピングできると主張するかもしれませんが、順序に依存することはできません。

(Map、<T>)ではなく、アノテーションのないパラメーターを持つ複数のメソッド引数を持つメソッドシグネチャーは、あいまいな状態になり、例外をトリガーします。

次の一連の例はそれぞれ、あいまいさをもたらす複数のメソッドを示しています。

複数のメソッドを持つメッセージハンドラーは、前の例で説明したのと同じルールに基づいてマップされます。ただし、一部のシナリオは依然として混乱を招く可能性があります。

次の例は、正当な(マッピング可能かつ明確な)署名を持つ複数のメソッドを示しています。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(Map m);
}

(メソッドの名前が同じでも、異なる名前でも違いはありません)。Message は、どちらの方法にもマッピングできます。最初のメソッドは、メッセージペイロードを str にマップでき、メッセージヘッダーを m にマップできるときに呼び出されます。2 番目の方法は、メッセージヘッダーのみを m にマッピングすることで候補にすることもできます。さらに悪いことに、両方のメソッドの名前は同じです。最初は、次の構成のためにあいまいに見える場合があります。

<int:service-activator input-channel="input" output-channel="output" method="doSomething">
    <bean class="org.things.Something"/>
</int:service-activator>

マッピングは最初にペイロードに基づいており、次に他のすべてに基づいているため機能します。つまり、最初の引数をペイロードにマッピングできるメソッドは、他のすべてのメソッドよりも優先されます。

次に、真に曖昧な状態を生成する別の例を考えてみましょう。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(String str);
}

どちらの方法にも、メッセージペイロードにマップできるシグネチャーがあります。彼らも同じ名前を持っています。このようなハンドラーメソッドは例外をトリガーします。ただし、メソッド名が異なる場合は、method 属性を使用してマッピングに影響を与えることができます(次の例に示す)。次の例は、2 つの異なるメソッド名を使用した同じ例を示しています。

public class Something {
    public String doSomething(String str, Map m);

    public String doSomethingElse(String str);
}

次の例は、method 属性を使用してマッピングを指示する方法を示しています。

<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse">
    <bean class="org.bar.Foo"/>
</int:service-activator>

構成では doSomethingElse メソッドを明示的にマップするため、あいまいさはなくなりました。