路由器實作
由於基於內容的路由通常需要一些特定領域的邏輯,因此大多數使用案例都需要 Spring Integration 的選項,透過使用 XML 命名空間支援或註解委派給 POJO。這兩者稍後都會討論。但是,我們先介紹幾個滿足常見要求的實作。
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>
Spring Integration 提供的命名空間也支援 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 時,有兩個選項。
首先,您可以定義路由器物件,如前面的範例所示
@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
時,會使用要評估的標頭名稱初始化它。標頭的值可能是以下兩種情況之一
-
任意值
-
通道名稱
如果是任意值,則需要將這些標頭值額外映射到通道名稱。否則,不需要額外設定。
Spring Integration 提供了一個基於簡單命名空間的 XML 設定,用於設定 HeaderValueRouter
。以下範例示範了當需要將標頭值映射到通道時,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 時,有兩個選項。首先,您可以定義路由器物件,如前面的範例所示
@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 以來,解析通道的行為更加明確。例如,如果您省略 基本上,預設情況下,路由器必須能夠成功地將訊息路由到至少一個通道。如果您真的想要丟棄訊息,您還必須將 |
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' 旗標與發布-訂閱通道的效果相同,並且與發布-訂閱通道一樣,預設情況下在 recipient-list-router 上停用。有關更多資訊,請參閱 PublishSubscribeChannel 設定。 |
設定 RecipientListRouter
時,另一個方便的選項是使用 Spring 運算式語言 (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
提供了多個操作,以便在執行階段動態操作接收者。這些管理操作透過 @ManagedResource
註解由 RecipientListRouterManagement
呈現。它們可透過使用 控制匯流排 以及使用 JMX 獲得,如下例所示
<control-bus input-channel="controlBus"/>
<recipient-list-router id="simpleRouter" input-channel="routingChannelA">
<recipient channel="channel1"/>
</recipient-list-router>
<channel id="channel2"/>
messagingTemplate.convertAndSend(controlBus, "@'simpleRouter.handler'.addRecipient('channel2')");
從應用程式啟動開始,simpleRouter
只有一個 channel1
接收者。但在 addRecipient
命令之後,新增了 channel2
接收者。這是一個「註冊對訊息的一部分內容感興趣」的使用案例,當我們可能在某個時間段內對來自路由器的訊息感興趣時,因此我們訂閱了 recipient-list-router
,並在某個時候決定取消訂閱。
由於 <recipient-list-router>
的執行階段管理操作,因此可以從一開始就設定它,而無需任何 <recipient>
。在這種情況下,當沒有與訊息匹配的接收者時,RecipientListRouter
的行為是相同的。如果設定了 defaultOutputChannel
,則訊息會傳送到那裡。否則,會擲回 MessageDeliveryException
。
XPath 路由器
XPath 路由器是 XML 模組的一部分。請參閱 使用 XPath 路由 XML 訊息。
路由和錯誤處理
Spring Integration 也提供了一種特殊的基於類型的路由器,稱為 ErrorMessageExceptionTypeRouter
,用於路由錯誤訊息(定義為 payload
是 Throwable
實例的訊息)。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" />