此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Integration 6.5.1spring-doc.cadn.net.cn

动态路由器

Spring Integration 为常见的基于内容的路由用例提供了许多不同的路由器配置,以及将自定义路由器实现为 POJO 的选项。 例如PayloadTypeRouter提供了一种简单的方法来配置路由器,该路由器根据传入消息的有效负载类型计算通道,而HeaderValueRouter在配置路由器时提供了相同的便利,该路由器通过评估特定消息标头的值来计算通道。还有基于表达式 (SpEL) 的路由器,其中通道是根据计算表达式确定的。所有这些类型的路由器都表现出一些动态特征。spring-doc.cadn.net.cn

但是,这些路由器都需要静态配置。即使在基于表达式的路由器的情况下,表达式本身也被定义为路由器配置的一部分,这意味着对相同值进行作的相同表达式始终会导致同一通道的计算。这在大多数情况下是可以接受的,因为此类路由是定义明确的,因此是可预测的。但是有时我们需要动态更改路由器配置,以便消息流可以路由到不同的通道。spring-doc.cadn.net.cn

例如,您可能希望关闭系统的某些部分以进行维护,并临时将消息重新路由到不同的消息流。作为另一个示例,您可能希望通过添加另一个路由来处理更具体的java.lang.Number(在以下情况下PayloadTypeRouter).spring-doc.cadn.net.cn

不幸的是,使用静态路由器配置来实现这些目标中的任何一个,您将不得不关闭整个应用程序,更改路由器的配置(更改路由),然后恢复应用程序。这显然不是任何人想要的解决方案。spring-doc.cadn.net.cn

动态路由器模式描述了在不关闭系统或单个路由器的情况下动态更改或配置路由器的机制。spring-doc.cadn.net.cn

在我们深入了解 Spring Integration 如何支持动态路由的细节之前,我们需要考虑路由器的典型流程:spring-doc.cadn.net.cn

  1. 计算通道标识符,这是路由器在收到消息后计算的值。通常,它是一个字符串或实际MessageChannel.spring-doc.cadn.net.cn

  2. 将通道标识符解析为通道名称。我们将在本节后面描述此过程的细节。spring-doc.cadn.net.cn

  3. 将通道名称解析为实际MessageChannelspring-doc.cadn.net.cn

如果步骤 1 导致MessageChannel,因为MessageChannel是任何路由器作业的最终产品。但是,如果第一步导致通道标识符不是MessageChannel,你有很多可能的方法来影响MessageChannel. 考虑以下有效负载类型路由器的示例:spring-doc.cadn.net.cn

<int:payload-type-router input-channel="routingChannel">
    <int:mapping type="java.lang.String"  channel="channel1" />
    <int:mapping type="java.lang.Integer" channel="channel2" />
</int:payload-type-router>

在有效负载类型路由器的上下文中,前面提到的三个步骤将实现如下:spring-doc.cadn.net.cn

  1. 计算作为有效负载类型的完全限定名称的通道标识符(例如java.lang.String).spring-doc.cadn.net.cn

  2. 将通道标识符解析为通道名称,其中上一步的结果用于从mapping元素。spring-doc.cadn.net.cn

  3. 将通道名称解析为MessageChannel作为对应用程序上下文中 bean 的引用(希望是MessageChannel)由上一步的结果标识。spring-doc.cadn.net.cn

换句话说,每个步骤都为下一步提供信息,直到该过程完成。spring-doc.cadn.net.cn

现在考虑一个标头值路由器的示例:spring-doc.cadn.net.cn

<int:header-value-router input-channel="inputChannel" header-name="testHeader">
    <int:mapping value="foo" channel="fooChannel" />
    <int:mapping value="bar" channel="barChannel" />
</int:header-value-router>

现在我们可以考虑这三个步骤如何适用于标头值路由器:spring-doc.cadn.net.cn

  1. 计算一个通道标识符,该标识符是由header-name属性。spring-doc.cadn.net.cn

  2. 将通道标识符解析为通道名称,其中上一步的结果用于从mapping元素。spring-doc.cadn.net.cn

  3. 将通道名称解析为MessageChannel作为对应用程序上下文中 bean 的引用(希望是MessageChannel)由上一步的结果标识。spring-doc.cadn.net.cn

两种不同路由器类型的上述两种配置看起来几乎相同。 但是,如果您查看HeaderValueRouter我们清楚地看到没有mappingsub 元素,如以下列表所示:spring-doc.cadn.net.cn

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

但是,该配置仍然完全有效。 那么自然的问题是第二步的映射呢?spring-doc.cadn.net.cn

第二步现在是可选的。 如果mapping未定义,则在第一步中计算的通道标识符值将自动被视为channel name,现在解析为实际的MessageChannel,如第三步。 这也意味着第二步是向路由器提供动态特征的关键步骤之一,因为它引入了一个过程,允许您更改通道标识符解析为通道名称的方式,从而影响确定MessageChannel从初始通道标识符。spring-doc.cadn.net.cn

例如,在前面的配置中,假设testHeadervalue 是 'kermit',它现在是通道标识符(第一步)。 由于此路由器中没有映射,因此无法将此信道标识符解析为信道名称(第二步),并且此信道标识符现在被视为信道名称。 但是,如果存在映射但值不同怎么办? 最终结果仍然是相同的,因为如果无法通过将通道标识符解析为通道名称的过程确定新值,则通道标识符将成为通道名称。spring-doc.cadn.net.cn

剩下的就是第三步将通道名称(“kermit”)解析为MessageChannel由此名称标识。 这基本上涉及对所提供名称的 bean 查找。 现在,所有包含标头值对的邮件testHeader=kermit将被路由到MessageChannel其 bean 名称(其id) 是“kermit”。spring-doc.cadn.net.cn

但是,如果您想将这些消息路由到“辛普森”频道怎么办?显然,更改静态配置是有效的,但这样做也需要关闭您的系统。 但是,如果您有权访问通道标识符映射,则可以在标头值对现在所在的位置引入新的映射kermit=simpson,从而让第二步将“kermit”视为通道标识符,同时将其解析为“simpson”作为通道名称。spring-doc.cadn.net.cn

这显然也适用于PayloadTypeRouter,您现在可以在其中重新映射或删除特定的有效负载类型映射。 事实上,它适用于所有其他路由器,包括基于表达式的路由器,因为它们的计算值现在有机会通过第二步解析为实际值channel name.spring-doc.cadn.net.cn

任何属于AbstractMappingMessageRouter(包括大多数框架定义的路由器)是动态路由器,因为channelMappingAbstractMappingMessageRouter水平。 该映射的 setter 方法与 'setChannelMapping' 和 'removeChannelMapping' 方法一起作为公共方法公开。 这些允许您在运行时更改、添加和删除路由器映射,只要您有对路由器本身的引用。 这也意味着您可以通过 JMX(参见 JMX 支持)或 Spring Integration 控制总线(参见 Control Bus)功能公开这些相同的配置选项。spring-doc.cadn.net.cn

回退到频道键,因为频道名称灵活方便。 但是,如果您不信任消息创建者,则恶意参与者 (了解系统) 可能会创建路由到意外通道的消息。 例如,如果密钥设置为路由器输入通道的通道名称,则此类消息将路由回路由器,最终导致堆栈溢出错误。 因此,您可能希望禁用此功能(将channelKeyFallback属性设置为false),并根据需要更改映射。

使用控制总线管理路由器映射

管理路由器映射的一种方法是通过控制总线模式,它公开了一个控制通道,您可以向该通道发送控制消息以管理和监视 Spring Integration 组件,包括路由器。spring-doc.cadn.net.cn

有关控制总线的更多信息,请参阅控制总线

通常,您将发送一条控制消息,要求在特定托管组件(如路由器)上调用特定作。 以下托管作(方法)特定于更改路由器解析过程:spring-doc.cadn.net.cn

  • public void setChannelMapping(String key, String channelName):允许您在channel identifierchannel namespring-doc.cadn.net.cn

  • public void removeChannelMapping(String key):允许您删除特定的通道映射,从而断开channel identifierchannel namespring-doc.cadn.net.cn

请注意,这些方法可用于简单的更改(例如更新单个路由或添加或删除路由)。 但是,如果要删除一条路由并添加另一条路由,则更新不是原子的。 这意味着路由表在更新之间可能处于不确定状态。 从 4.0 版开始,您现在可以使用控制总线以原子方式更新整个路由表。 以下方法可让您这样做:spring-doc.cadn.net.cn

  • public Map<String, String>getChannelMappings():返回当前映射。spring-doc.cadn.net.cn

  • public void replaceChannelMappings(Properties channelMappings):更新映射。 请注意,channelMappings参数是一个Properties对象,因此必须将其添加到相应的IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS页眉:spring-doc.cadn.net.cn

Properties newMapping = new Properties();
newMapping.setProperty("foo", "bar");
newMapping.setProperty("baz", "qux");
Message<?> replaceChannelMappingsCommandMessage =
                     MessageBuilder.withPayload("'router.handler'.replaceChannelMappings")
                            .setHeader(IntegrationMessageHeaderAccessor.CONTROL_BUS_ARGUMENTS, List.of(newMapping))
                            .build();

对于对地图进行编程更改,我们建议您使用setChannelMappings方法,出于类型安全考虑。replaceChannelMappings忽略不是的键或值String对象。spring-doc.cadn.net.cn

使用 JMX 管理路由器映射

您还可以使用 Spring 的 JMX 支持来公开路由器实例,然后使用您最喜欢的 JMX 客户端(例如 JConsole)来管理这些作(方法)以更改路由器的配置。spring-doc.cadn.net.cn

有关 Spring Integration 的 JMX 支持的更多信息,请参阅 JMX 支持