路由器实现

由于基于内容的路由通常需要一些特定于域的逻辑,因此大多数用例都需要 Spring 集成的选项,通过使用 XML 名称空间支持或注释来委托给 POJO。 这两者都将在后面讨论。 但是,我们首先介绍几个满足常见要求的实现。spring-doc.cn

PayloadTypeRouter

A 将消息发送到由有效负载类型映射定义的通道,如下例所示:PayloadTypeRouterspring-doc.cn

<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 提供的命名空间也支持配置(参见),它通过将配置及其相应的实现(通过使用元素定义)组合成一个更简洁的配置元素,从根本上简化了配置。 以下示例显示了一个等效于上述配置但使用命名空间支持的配置:PayloadTypeRouterNamespace Support<router/><bean/>PayloadTypeRouterspring-doc.cn

<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 中配置的等效路由器:spring-doc.cn

@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 时,有两个选项。spring-doc.cn

首先,您可以定义 router 对象,如前面的示例所示:spring-doc.cn

@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@Beanspring-doc.cn

其次,您可以在 DSL 流本身中定义路由函数,如下例所示:spring-doc.cn

@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 根据各个标头值映射将 Messages 发送到通道。 创建 a 时,将使用要评估的标头的名称对其进行初始化。 标头的值可以是以下两项之一:HeaderValueRouterHeaderValueRouterspring-doc.cn

如果它是任意值,则需要将这些 Headers 值附加到通道名称的其他映射。 否则,无需其他配置。spring-doc.cn

Spring 集成提供了一个简单的基于名称空间的 XML 配置来配置一个 . 以下示例演示了当需要将 Headers 值映射到 channels 时的配置:HeaderValueRouterHeaderValueRouterspring-doc.cn

<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-channelresolution-requiredfalsespring-doc.cn

通常,标头值未显式映射到通道的消息将发送到 。 但是,当 Headers 值映射到通道名称但无法解析通道时,将属性设置为会导致将此类消息路由到 .default-output-channelresolution-requiredfalsedefault-output-channelspring-doc.cn

以下示例显示了在 Java 中配置的等效路由器:spring-doc.cn

@ServiceActivator(inputChannel = "routingChannel")
@Bean
public HeaderValueRouter router() {
    HeaderValueRouter router = new HeaderValueRouter("testHeader");
    router.setChannelMapping("someHeaderValue", "channelA");
    router.setChannelMapping("someOtherHeaderValue", "channelB");
    return router;
}

使用 Java DSL 时,有两个选项。 首先,您可以定义 router 对象,如前面的示例所示:spring-doc.cn

@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@Beanspring-doc.cn

其次,您可以在 DSL 流本身中定义路由函数,如下例所示:spring-doc.cn

@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();
}

不需要将 Headers 值映射到通道名称的配置,因为 Headers 值本身表示通道名称。 以下示例显示了一个不需要将 Headers 值映射到通道名称的路由器:spring-doc.cn

<int:header-value-router input-channel="routingChannel" header-name="testHeader"/>

从 Spring Integration 2.1 开始,解析通道的行为更加明确。 例如,如果省略该属性,则路由器无法解析至少一个有效通道,并且通过设置为 忽略任何通道名称解析失败,然后抛出 a。default-output-channelresolution-requiredfalseMessageDeliveryExceptionspring-doc.cn

基本上,默认情况下,路由器必须能够成功地将消息路由到至少一个通道。 如果您确实要删除消息,还必须设置为 。default-output-channelnullChannelspring-doc.cn

RecipientListRouter

A 将收到的每条消息发送到静态定义的消息通道列表。 以下示例创建一个 :RecipientListRouterRecipientListRouterspring-doc.cn

<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 集成还为配置提供了名称空间支持(参见名称空间支持),如下例所示:RecipientListRouterspring-doc.cn

<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 中配置的等效路由器:spring-doc.cn

@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 配置的等效路由器:spring-doc.cn

@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 表达式语言 (SpEL) 支持作为单个收件人通道的选择器。 这样做类似于在 'chain' 的开头使用 filter 来充当 “selective consumer”。 但是,在这种情况下,它全部相当简洁地组合到路由器的配置中,如下例所示:RecipientListRouterspring-doc.cn

<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 表达式,以确定是否应将此收件人包含在给定 input 消息的收件人列表中。 表达式的求值结果必须为 。 如果未定义此属性,则渠道始终位于收件人列表中。selector-expressionbooleanspring-doc.cn

RecipientListRouterManagement

从版本 4.1 开始,提供了几个操作来在运行时动态操作收件人。 这些管理操作是通过 annotation 呈现的。 它们可以通过使用 Control Bus 和 JMX 来使用,如下例所示:RecipientListRouterRecipientListRouterManagement@ManagedResourcespring-doc.cn

<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();

从应用程序启动 中,只有一个收件人。 但在命令之后,将添加 recipient。 这是一个 “注册对消息中的一部分的兴趣 ”的用例,当我们可能在某个时间段对来自路由器的消息感兴趣时,因此我们正在订阅 ,并在某个时候决定取消订阅。simpleRouterchannel1addRecipientchannel2recipient-list-routerspring-doc.cn

由于 的运行时管理操作 ,它可以从一开始就配置,而无需任何操作。 在这种情况下,当消息没有一个匹配的收件人时,其行为是相同的。 如果已配置,则消息将发送到该处。 否则,将抛出 the 。<recipient-list-router><recipient>RecipientListRouterdefaultOutputChannelMessageDeliveryExceptionspring-doc.cn

XPath 路由器

XPath Router 是 XML Module 的一部分。 请参见使用 XPath 路由 XML 消息spring-doc.cn

路由和错误处理

Spring 集成还提供了一个特殊的基于类型的路由器,称为路由错误消息(定义为其实例的消息)。 类似于 . 事实上,它们几乎相同。 唯一的区别是,在导航有效负载实例的实例层次结构(例如)以查找最具体的类型和通道映射时,导航“异常原因”(例如)的层次结构以查找最具体的类型或通道映射,并用于匹配类或任何超类。ErrorMessageExceptionTypeRouterpayloadThrowableErrorMessageExceptionTypeRouterPayloadTypeRouterPayloadTypeRouterpayload.getClass().getSuperclass()ErrorMessageExceptionTypeRouterpayload.getCause()ThrowablemappingClass.isInstance(cause)causespring-doc.cn

在这种情况下,通道映射顺序很重要。 因此,如果需要获取 , 而不是 的映射,则必须先在路由器上配置最后一个映射。IllegalArgumentExceptionRuntimeException
从版本 4.3 开始,在初始化阶段加载所有映射类,以便快速失败。ErrorMessageExceptionTypeRouterClassNotFoundException

以下示例显示了 的示例配置:ErrorMessageExceptionTypeRouterspring-doc.cn

@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" />