ルーターの実装
コンテンツベースのルーティングにはドメイン固有のロジックが必要になることが多いため、ほとんどのユースケースでは、XML 名前空間のサポートまたはアノテーションを使用して POJO に委譲するための Spring Integration のオプションが必要です。これらの両方については、後で説明します。ただし、最初に一般的な要件を満たす実装をいくつか紹介します。
PayloadTypeRouter
次の例に示すように、PayloadTypeRouter
はペイロード型マッピングで定義されたチャネルにメッセージを送信します。
<bean id="payloadTypeRouter"
class="org.springframework.integration.router.PayloadTypeRouter">
<property name="channelMapping">
<map>
<entry key="java.lang.String" value-ref="stringChannel"/>
<entry key="java.lang.Integer" value-ref="integerChannel"/>
</map>
</property>
</bean>
PayloadTypeRouter
の構成は、Spring Integration(Namespace Support
を参照)によって提供される名前空間によってもサポートされます。PayloadTypeRouter
の構成は、<router/>
構成とそれに対応する実装(<bean/>
要素を使用して定義)を単一のより簡潔な構成要素に組み合わせることにより、構成を本質的に簡素化します。次の例は、上記のものと同等ですが、名前空間サポートを使用する PayloadTypeRouter
構成を示しています。
<int:payload-type-router input-channel="routingChannel">
<int:mapping type="java.lang.String" channel="stringChannel" />
<int:mapping type="java.lang.Integer" channel="integerChannel" />
</int:payload-type-router>
次の例は、Java で構成された同等のルーターを示しています。
@ServiceActivator(inputChannel = "routingChannel")
@Bean
public PayloadTypeRouter router() {
PayloadTypeRouter router = new PayloadTypeRouter();
router.setChannelMapping(String.class.getName(), "stringChannel");
router.setChannelMapping(Integer.class.getName(), "integerChannel");
return router;
}
Java DSL を使用する場合、2 つのオプションがあります。
最初に、前の例に示すようにルーターオブジェクトを定義できます。
@Bean
public IntegrationFlow routerFlow1() {
return IntegrationFlow.from("routingChannel")
.route(router())
.get();
}
public PayloadTypeRouter router() {
PayloadTypeRouter router = new PayloadTypeRouter();
router.setChannelMapping(String.class.getName(), "stringChannel");
router.setChannelMapping(Integer.class.getName(), "integerChannel");
return router;
}
ルーターは @Bean
にできますが、そうする必要はありません。@Bean
でない場合、フローはそれを登録します。
次に、次の例に示すように、DSL フロー自体内でルーティング機能を定義できます。
@Bean
public IntegrationFlow routerFlow2() {
return IntegrationFlow.from("routingChannel")
.<Object, Class<?>>route(Object::getClass, m -> m
.channelMapping(String.class, "stringChannel")
.channelMapping(Integer.class, "integerChannel"))
.get();
}
HeaderValueRouter
HeaderValueRouter
は、個々のヘッダー値マッピングに基づいてメッセージをチャネルに送信します。HeaderValueRouter
が作成されると、評価されるヘッダーの名前で初期化されます。ヘッダーの値は、次の 2 つのいずれかです。
任意の値
チャンネル名
任意の値である場合、これらのヘッダー値からチャネル名への追加マッピングが必要です。それ以外の場合、追加の構成は必要ありません。
Spring Integration は、HeaderValueRouter
を構成するための単純な名前空間ベースの XML 構成を提供します。次の例は、チャネルへのヘッダー値のマッピングが必要な場合の HeaderValueRouter
の構成を示しています。
<int:header-value-router input-channel="routingChannel" header-name="testHeader">
<int:mapping value="someHeaderValue" channel="channelA" />
<int:mapping value="someOtherHeaderValue" channel="channelB" />
</int:header-value-router>
解決プロセス中に、前の例で定義されたルーターでチャネル解決エラーが発生し、例外が発生する場合があります。このような例外を抑制し、未解決のメッセージをデフォルトの出力チャネル(default-output-channel
属性で識別)に送信する場合は、resolution-required
を false
に設定します。
通常、ヘッダー値が明示的にチャネルにマッピングされていないメッセージは default-output-channel
に送信されます。ただし、ヘッダー値がチャネル名にマップされているが、チャネルを解決できない場合、resolution-required
属性を false
に設定すると、そのようなメッセージが default-output-channel
にルーティングされます。
次の例は、Java で構成された同等のルーターを示しています。
@ServiceActivator(inputChannel = "routingChannel")
@Bean
public HeaderValueRouter router() {
HeaderValueRouter router = new HeaderValueRouter("testHeader");
router.setChannelMapping("someHeaderValue", "channelA");
router.setChannelMapping("someOtherHeaderValue", "channelB");
return router;
}
Java DSL を使用する場合、2 つのオプションがあります。最初に、前の例に示すようにルーターオブジェクトを定義できます。
@Bean
public IntegrationFlow routerFlow1() {
return IntegrationFlow.from("routingChannel")
.route(router())
.get();
}
public HeaderValueRouter router() {
HeaderValueRouter router = new HeaderValueRouter("testHeader");
router.setChannelMapping("someHeaderValue", "channelA");
router.setChannelMapping("someOtherHeaderValue", "channelB");
return router;
}
ルーターは @Bean
にできますが、そうする必要はありません。@Bean
でない場合、フローはそれを登録します。
次に、次の例に示すように、DSL フロー自体内でルーティング機能を定義できます。
@Bean
public IntegrationFlow routerFlow2() {
return IntegrationFlow.from("routingChannel")
.route(Message.class, m -> m.getHeaders().get("testHeader", String.class),
m -> m
.channelMapping("someHeaderValue", "channelA")
.channelMapping("someOtherHeaderValue", "channelB"),
e -> e.id("headerValueRouter"))
.get();
}
ヘッダー値自体がチャネル名を表すため、ヘッダー値のチャネル名へのマッピングが不要な構成。次の例は、ヘッダー値をチャネル名にマッピングする必要のないルーターを示しています。
<int:header-value-router input-channel="routingChannel" header-name="testHeader"/>
Spring Integration 2.1 以降、チャネル解決の動作はより明確になりました。例: 基本的に、デフォルトでは、ルーターはメッセージを少なくとも 1 つのチャネルに正常にルーティングできる必要があります。メッセージを本当にドロップしたい場合は、 |
RecipientListRouter
RecipientListRouter
は、受信した各メッセージを静的に定義されたメッセージチャネルのリストに送信します。次の例では、RecipientListRouter
を作成します。
<bean id="recipientListRouter"
class="org.springframework.integration.router.RecipientListRouter">
<property name="channels">
<list>
<ref bean="channel1"/>
<ref bean="channel2"/>
<ref bean="channel3"/>
</list>
</property>
</bean>
Spring Integration は、次の例に示すように、RecipientListRouter
構成(名前空間サポートを参照)の名前空間サポートも提供します。
<int:recipient-list-router id="customRouter" input-channel="routingChannel"
timeout="1234"
ignore-send-failures="true"
apply-sequence="true">
<int:recipient channel="channel1"/>
<int:recipient channel="channel2"/>
</int:recipient-list-router>
次の例は、Java で構成された同等のルーターを示しています。
@ServiceActivator(inputChannel = "routingChannel")
@Bean
public RecipientListRouter router() {
RecipientListRouter router = new RecipientListRouter();
router.setSendTimeout(1_234L);
router.setIgnoreSendFailures(true);
router.setApplySequence(true);
router.addRecipient("channel1");
router.addRecipient("channel2");
router.addRecipient("channel3");
return router;
}
次の例は、Java DSL を使用して構成された同等のルーターを示しています。
@Bean
public IntegrationFlow routerFlow() {
return IntegrationFlow.from("routingChannel")
.routeToRecipients(r -> r
.applySequence(true)
.ignoreSendFailures(true)
.recipient("channel1")
.recipient("channel2")
.recipient("channel3")
.sendTimeout(1_234L))
.get();
}
ここでの 'apply-sequence' フラグは、publish-subscribe-channel の場合と同じ効果があり、publish-subscribe-channel の場合と同様に、recipient-list-router ではデフォルトで無効になっています。詳細については、PublishSubscribeChannel 設定を参照してください。 |
RecipientListRouter
を構成する際のもう 1 つの便利なオプションは、Spring Expression Language(SpEL)サポートを個々の受信者チャネルのセレクターとして使用することです。これは、「チェーン」の先頭で「選択的コンシューマー」として機能するフィルターを使用することに似ています。ただし、この場合、次の例に示すように、すべてルーターの構成にかなり簡潔に結合されます。
<int:recipient-list-router id="customRouter" input-channel="routingChannel">
<int:recipient channel="channel1" selector-expression="payload.equals('foo')"/>
<int:recipient channel="channel2" selector-expression="headers.containsKey('bar')"/>
</int:recipient-list-router>
前の構成では、selector-expression
属性によって識別される SpEL 式が評価され、この受信者を特定の入力メッセージの受信者リストに含める必要があるかどうかが判断されます。式の評価結果は boolean
でなければなりません。この属性が定義されていない場合、チャネルは常に受信者のリストに含まれます。
RecipientListRouterManagement
バージョン 4.1 以降、RecipientListRouter
は、実行時に動的に受信者を操作するためのいくつかの操作を提供します。これらの管理操作は、RecipientListRouterManagement
によって @ManagedResource
アノテーションを介して提示されます。次の例に示すように、制御バスと JMX を使用して使用できます。
<control-bus input-channel="controlBus"/>
<recipient-list-router id="simpleRouter" input-channel="routingChannelA">
<recipient channel="channel1"/>
</recipient-list-router>
<channel id="channel2"/>
Message<?> addRecipientCommandMessage =
MessageBuilder.withPayload("'simpleRouter.handler'.addRecipient")
.setHeader(IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS, List.of("channel2"))
.build();
アプリケーションから simpleRouter
を起動し、channel1
受信者は 1 人だけです。ただし、addRecipient
コマンドの後に、channel2
受信者が追加されます。これは「メッセージの一部である何かへの関心の登録」のユースケースであり、ある期間にルーターからのメッセージに関心がある可能性があるため、recipient-list-router
をサブスクライブし、ある時点でサブスクライブを解除することにします。
<recipient-list-router>
のランタイム管理操作により、最初から <recipient>
なしで構成できます。この場合、メッセージに一致する受信者がいない場合の RecipientListRouter
の動作は同じです。defaultOutputChannel
が構成されている場合、メッセージはそこに送信されます。それ以外の場合は、MessageDeliveryException
がスローされます。
XPath ルーター
XPath ルーターは XML モジュールの一部です。XPath を使用した XML メッセージのルーティングを参照してください。
ルーティングとエラー処理
Spring Integration は、ルーティングエラーメッセージ(payload
が Throwable
インスタンスであるメッセージとして定義される)のために、ErrorMessageExceptionTypeRouter
と呼ばれる特別な型ベースのルーターも提供します。ErrorMessageExceptionTypeRouter
は PayloadTypeRouter
に似ています。実際、それらはほとんど同一です。唯一の違いは、PayloadTypeRouter
がペイロードインスタンスのインスタンス階層(たとえば payload.getClass().getSuperclass()
)をナビゲートして最も具象型とチャネルマッピングを見つけるのに対し、ErrorMessageExceptionTypeRouter
は「例外原因」(たとえば payload.getCause()
)の階層をナビゲートして見つけることです最も具体的な Throwable
型またはチャネルマッピング。mappingClass.isInstance(cause)
を使用して、cause
をクラスまたはスーパークラスに一致させます。
この場合のチャネルマッピングの順序は重要です。そのため、IllegalArgumentException のマッピングを取得する必要があり、RuntimeException のマッピングを取得する必要がない場合、最初にルーターで最後の設定を行う必要があります。 |
バージョン 4.3 以降、ErrorMessageExceptionTypeRouter は初期化フェーズ中にすべてのマッピングクラスをロードして、ClassNotFoundException のフェイルファーストを行います。 |
次の例は、ErrorMessageExceptionTypeRouter
のサンプル構成を示しています。
Java DSL
Kotlin DSL
Groovy DSL
XML DSL
@Bean
public IntegrationFlow someFlow() {
return f -> f
.routeByException(r -> r
.channelMapping(IllegalArgumentException.class, "illegalChannel")
.channelMapping(NullPointerException.class, "npeChannel")
.defaultOutputChannel("defaultChannel"));
}
@Bean
fun someFlow() =
integrationFlow {
routeByException {
channelMapping(IllegalArgumentException::class.java, "illegalChannel")
channelMapping(NullPointerException::class.java, "npeChannel")
defaultOutputChannel("defaultChannel")
}
}
@Bean
someFlow() {
integrationFlow {
routeByException {
channelMapping IllegalArgumentException, 'illegalChannel'
channelMapping NullPointerException, 'npeChannel'
defaultOutputChannel 'defaultChannel'
}
}
}
<int:exception-type-router input-channel="inputChannel"
default-output-channel="defaultChannel">
<int:mapping exception-type="java.lang.IllegalArgumentException"
channel="illegalChannel"/>
<int:mapping exception-type="java.lang.NullPointerException"
channel="npeChannel"/>
</int:exception-type-router>
<int:channel id="illegalChannel" />
<int:channel id="npeChannel" />