Router Implementations
由于基于内容的路由通常需要一些特定于域的逻辑,因此大多数用例都需要 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 还通过 Namespace Support
提供的命名空间支持 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
HeaderValueRouter
根据各个标头值映射将消息发送到通道。创建 HeaderValueRouter
时,使用要评估的标头的名称对其进行初始化。标头值可能是两种情况之一:
-
An arbitrary value
-
A channel name
如果是任意值,则需要针对这些标头值到通道名称的其他映射。否则,无需其他配置。
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 还通过 Namespace Support 提供 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();
}
此处的“应用顺序”标志与发布/订阅通道上的标志有相同的效果,并且与发布/订阅通道一样,它在 |
在配置 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
提供了多个操作,以在运行时动态操作收件人。这些管理操作由 RecipientListRouterManagement
通过 @ManagedResource
注释提供。可以使用 Control Bus 以及 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 Router
XPath 路由器是 XML 模块的一部分。请参阅 Routing XML Messages with XPath。
Routing and Error Handling
Spring Integration 还提供了一种特殊的基于类型的路由器,称为 ErrorMessageExceptionTypeRouter
,用于路由错误消息(定义为 payload
是 Throwable
实例的消息)。ErrorMessageExceptionTypeRouter
类似于 PayloadTypeRouter
。事实上,它们几乎相同。唯一的区别在于,当 PayloadTypeRouter
浏览有效负载实例的实例层次结构(例如,payload.getClass().getSuperclass()
)以找到最具体的类型和通道映射时,ErrorMessageExceptionTypeRouter
浏览“异常原因”的层次结构(例如,payload.getCause()
)以找到最具体的 Throwable
类型或通道映射,并使用 mappingClass.isInstance(cause)
将 cause
与类或任何超类匹配。
这种情况下,信道映射顺序很重要。因此,如果要求获取 IllegalArgumentException
的映射,但不能获取 RuntimeException
的映射,则必须先在路由器上配置最后一个映射。
自从版本 4.3 起, |
以下示例显示了 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" />