对于最新稳定版本,请使用 Spring Integration 7.0.0spring-doc.cadn.net.cn

路由器实现

由于基于内容的路由通常需要某些域特定的逻辑,大多数用例都需要 Spring Integration 的选项,通过 XML 命名空间支持或注释来委派给 POJO。 这两点后文都会讨论。 不过,我们首先介绍几个满足共同需求的实现。spring-doc.cadn.net.cn

有效载荷类型路由器

一个有效载荷类型路由器向由有效载荷类型映射定义的信道发送消息,如下示例所示:spring-doc.cadn.net.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 提供的命名空间支持命名空间支持),本质上通过将<路由器/>配置及其对应实现(通过使用<豆/>元素)变成一个单一且更简洁的配置元素。 以下示例展示了一个有效载荷类型路由器配置等价于上述配置,但使用命名空间支持:spring-doc.cadn.net.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.cadn.net.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.cadn.net.cn

首先,你可以像前面示例所示定义路由器对象:spring-doc.cadn.net.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. 如果流不是@Bean.spring-doc.cadn.net.cn

其次,你可以在DSL流程本身中定义路由功能,如下示例所示:spring-doc.cadn.net.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();
}

头值路由器

一个头值路由器根据各个头值映射向信道发送消息。 当头值路由器创建时,初始化时会以待评估的头部名称。 头部的值可能有两种情况之一:spring-doc.cadn.net.cn

如果是任意值,则需要将这些头值与信道名称进行额外的映射。 否则,无需额外配置。spring-doc.cadn.net.cn

Spring Integration 提供了一个简单的基于命名空间的 XML 配置,用于配置头值路由器. 以下示例展示了头值路由器当需要将头部值映射到信道时:spring-doc.cadn.net.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>

在解析过程中,前述示例中定义的路由器可能会遇到通道解析失败,导致异常。 如果您想抑制此类异常,并将未解析消息发送到默认输出信道(标识为默认输出通道属性)集合分辨率要求false.spring-doc.cadn.net.cn

通常,头值未被显式映射到信道的消息会发送到默认输出通道. 然而,当头部值映射到信道名称但无法解析信道时,可以设置分辨率要求归属为false结果是将此类消息路由到默认输出通道.spring-doc.cadn.net.cn

以下示例展示了用 Java 配置的对应路由器:spring-doc.cadn.net.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 时,有两种选择。 首先,你可以像前面示例所示定义路由器对象:spring-doc.cadn.net.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. 如果流不是@Bean.spring-doc.cadn.net.cn

其次,你可以在DSL流程本身中定义路由功能,如下示例所示:spring-doc.cadn.net.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();
}

配置中不需要将头值映射到通道名称,因为头值本身代表通道名称。 以下示例展示了一种无需将头值映射到信道名称的路由器:spring-doc.cadn.net.cn

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

自 Spring Integration 2.1 起,通道解析的行为变得更加明确。 例如,如果你省略默认输出通道该属性,路由器无法解析至少一个有效信道,且任何信道名称解析失败都被忽略,方法是设置分辨率要求false,然后MessageDeliveryException被抛出。spring-doc.cadn.net.cn

基本上,默认情况下,路由器必须能够成功将消息路由到至少一个信道。 如果你真的想留言,你也必须有默认输出通道设置为零通道.spring-doc.cadn.net.cn

收件人列表路由器

一个收件人列表路由器将每个收到的消息发送到一个静态定义的消息通道列表。 以下示例可生成收件人列表路由器:spring-doc.cadn.net.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 集成还为收件人列表路由器配置(参见命名空间支持),如下示例所示:spring-doc.cadn.net.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.cadn.net.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.cadn.net.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();
}
这里的“应用序列”标志与发布-订阅频道的效果相同,且与发布-订阅频道一样,默认情况下是收件人列表路由器. 看发布订阅频道配置更多信息请见。

配置时另一个方便的选项收件人列表路由器是使用Spring表达语言(SpEL)支持作为单个接收信道的选择器。 这样做类似于在“链条”开头使用过滤器作为“选择性消费者”。 然而,在这种情况下,这些信息被相当简洁地组合到路由器的配置中,如下示例所示:spring-doc.cadn.net.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>

在前述配置中,由选择表达式属性会被评估,以确定该收件人是否应包含在给定输入消息的收件人列表中。 表达式的评估结果必须是布尔. 如果该属性未定义,该信道总是在接收者列表中。spring-doc.cadn.net.cn

收件人列表路由器管理

从4.1版本开始,收件人列表路由器提供多种作,在运行时动态作接收者。 这些管理作由以下方式呈现收件人列表路由器管理通过@ManagedResource注解。 它们可以通过控制总线和JMX两种方式获得,如下示例所示:spring-doc.cadn.net.cn

<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只有一个频道1收件人。 但在addRecipient命令频道2收件人被添加。 这是一种“注册对消息中某项内容感兴趣”的用例,当我们可能在某个时间点对路由器发送的消息感兴趣时,我们订阅了收件人列表路由器然后,在某个时候,决定退订。spring-doc.cadn.net.cn

由于运行时管理作的关系<收件人列表路由器>,它可以在没有任何情况下进行配置<收件人>从一开始。 在这种情况下,的行为收件人列表路由器当消息没有匹配的接收者时,也是相同的。 如果默认输出通道配置完成后,消息被发送到那里。 否则,MessageDeliveryException被抛出。spring-doc.cadn.net.cn

XPath 路由器

XPath 路由器是 XML 模块的一部分。 参见 XPath 中的 XML 消息路由spring-doc.cadn.net.cn

路由与错误处理

Spring Integration 还提供一种特殊的基于类型的路由器,称为错误消息异常类型路由器用于路由错误消息(定义为有效载荷可投掷实例)。错误消息异常类型路由器有效载荷类型路由器. 事实上,它们几乎一模一样。 唯一的区别是,虽然有效载荷类型路由器导航有效载荷实例的实例层级(例如,payload.getClass().getSuperclass())以找到最具体的类型和信道映射,即错误消息异常类型路由器在“异常原因”层级中导航(例如,payload.getCause())以找到最具体的可投掷类型或信道映射及用途mappingClass.isInstance(原因)以匹配原因无论是班级还是任何超级班级。spring-doc.cadn.net.cn

在这种情况下,通道映射顺序很重要。 所以,如果需要获取映射以获取IllegalArgumentException,但不是运行异常最后一个必须先在路由器上配置。
自4.3版本起错误消息异常类型路由器在初始化阶段加载所有映射类,使其实现故障快速ClassNotFoundException.

下例展示了 的示例配置错误消息异常类型路由器:spring-doc.cadn.net.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" />