此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Integration 6.3.1! |
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Integration 6.3.1! |
由于基于内容的路由通常需要一些特定于域的逻辑,因此大多数用例都需要 Spring Integration 的选项,以便通过使用 XML 命名空间支持或注释来委托给 POJO。 这两者都将在后面讨论。 但是,我们首先介绍几个满足常见要求的实现。
PayloadTypeRouter
A 将消息发送到 payload-type 映射定义的通道,如以下示例所示: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
Namespace Support
<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
A 根据各个标头值映射将消息发送到通道。
创建 a 时,将使用要计算的标头的名称对其进行初始化。
标头的值可以是以下两种情况之一: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 开始,解析通道的行为更加明确。
例如,如果省略该属性,则路由器无法解析至少一个有效信道,并且通过设置为 忽略任何信道名称解析失败,则抛出 a。 基本上,默认情况下,路由器必须能够将消息成功路由到至少一个通道。
如果确实要丢弃消息,还必须设置为 。 |
从 Spring Integration 2.1 开始,解析通道的行为更加明确。
例如,如果省略该属性,则路由器无法解析至少一个有效信道,并且通过设置为 忽略任何信道名称解析失败,则抛出 a。 基本上,默认情况下,路由器必须能够将消息成功路由到至少一个通道。
如果确实要丢弃消息,还必须设置为 。 |
RecipientListRouter
A 将每条接收到的消息发送到静态定义的消息通道列表。
以下示例创建一个: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 一样,默认情况下,它在 .
有关详细信息,请参阅 PublishSubscribeChannel 配置。recipient-list-router |
配置时的另一个方便选项是使用 Spring Expression 语言 (SpEL) 支持作为单个收件人通道的选择器。
这样做类似于在“链”的开头使用过滤器来充当“选择性消费者”。
但是,在这种情况下,它们都相当简洁地组合到路由器的配置中,如以下示例所示:RecipientListRouter
<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>
在前面的配置中,将评估由属性标识的 SpEL 表达式,以确定是否应将此收件人包含在给定输入邮件的收件人列表中。
表达式的计算结果必须是 。
如果未定义此属性,则频道始终位于收件人列表中。selector-expression
boolean
此处的“apply-sequence”标志与 publish-subscribe-channel 的效果相同,并且与 publish-subscribe-channel 一样,默认情况下,它在 .
有关详细信息,请参阅 PublishSubscribeChannel 配置。recipient-list-router |
RecipientListRouterManagement
从版本 4.1 开始,提供了几个操作来在运行时动态操作收件人。
这些管理操作通过注释呈现。
它们可以通过使用控制总线以及使用 JMX 来使用,如以下示例所示:RecipientListRouter
RecipientListRouterManagement
@ManagedResource
<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" />
在这种情况下,通道映射顺序很重要。
因此,如果需要获取 的映射,但不是 ,则必须首先在路由器上配置最后一个。IllegalArgumentException RuntimeException |
从版本 4.3 开始,在初始化阶段将所有映射类加载到快速失效状态。ErrorMessageExceptionTypeRouter ClassNotFoundException |