# 消息路由
## 消息路由
本章介绍了使用 Spring 集成路由消息的细节。
### 路由器
本节介绍路由器的工作方式。它包括以下主题:
* [Overview](#router-overview)
* [通用路由器参数](#router-common-parameters)
* [路由器实现](#router-implementations)
* [配置通用路由器](#router-namespace)
* [Routers and the Spring Expression Language (SpEL)](#router-spel)
* [动态路由器](#dynamic-routers)
#### 概述
路由器是许多消息传递体系结构中的关键元素。它们消耗来自消息通道的消息,并根据一组条件将每个消耗的消息转发到一个或多个不同的消息通道。
Spring 集成提供了以下路由器:
* [有效载荷型路由器](#router-implementations-payloadtyperouter)
* [头值路由器](#router-implementations-headervaluerouter)
* [收件人列表路由器](#router-implementations-recipientlistrouter)
* [XPath 路由器(XML 模块的一部分)](./xml.html#xml-xpath-routing)
* [错误消息异常类型路由器](#router-implementations-exception-router)
* [(通用)路由器](#router-namespace)
路由器实现共享许多配置参数。然而,路由器之间存在某些差异。此外,配置参数的可用性取决于路由器是在链内使用还是在链外使用。为了提供一个快速的概述,所有可用的属性都在下面的两个表中列出。
下表显示了链外路由器可用的配置参数:
| Attribute |路由器| header value router | xpath router | payload type router | recipient list route | exception type router |
|----------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|
| apply-sequence |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
|default-output-channel|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| resolution-required |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| ignore-send-failures |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| timeout |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| id |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| auto-startup |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| input-channel |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| order |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| method |![tickmark](images/tickmark.png)| | | | | |
| ref |![tickmark](images/tickmark.png)| | | | | |
| expression |![tickmark](images/tickmark.png)| | | | | |
| header-name | |![tickmark](images/tickmark.png)| | | | |
| evaluate-as-string | | |![tickmark](images/tickmark.png)| | | |
| xpath-expression-ref | | |![tickmark](images/tickmark.png)| | | |
| converter | | |![tickmark](images/tickmark.png)| | | |
下表显示了链内路由器可用的配置参数:
| Attribute |路由器| header value router | xpath router | payload type router | recipient list router | exception type router |
|----------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|
| apply-sequence |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
|default-output-channel|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| resolution-required |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| ignore-send-failures |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| timeout |![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|![tickmark](images/tickmark.png)|
| id | | | | | | |
| auto-startup | | | | | | |
| input-channel | | | | | | |
| order | | | | | | |
| method |![tickmark](images/tickmark.png)| | | | | |
| ref |![tickmark](images/tickmark.png)| | | | | |
| expression |![tickmark](images/tickmark.png)| | | | | |
| header-name | |![tickmark](images/tickmark.png)| | | | |
| evaluate-as-string | | |![tickmark](images/tickmark.png)| | | |
| xpath-expression-ref | | |![tickmark](images/tickmark.png)| | | |
| converter | | |![tickmark](images/tickmark.png)| | | |
| |在 Spring Integration2.1 中,路由器参数已经在所有路由器实现中得到了更多的标准化。因此,一些小的更改可能会破坏较旧的 Spring 基于 Integration 的应用程序。,自 Spring Integration2.1 以来,将`ignore-channel-name-resolution-failures`属性删除,以利于将其行为与`resolution-required`属性合并,
此外,`resolution-required`属性现在默认为`true`,
在进行这些更改之前,`resolution-required`属性默认为`false`,当没有解析通道且没有设置`default-output-channel`时,导致消息被静默删除。
新行为需要至少一个解析通道,并且默认情况下,如果没有确定通道(或者发送尝试未成功),则抛出`MessageDeliveryException`。
如果你确实希望静默地删除消息,则可以设置`default-output-channel="nullChannel"`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### 常见路由器参数
本节描述所有路由器参数的公共参数(在本章前面的两个表中勾选了它们的所有方框的参数)。
##### 链条的内部和外部
以下参数对链内和链外的所有路由器都有效。
`apply-sequence`
此属性指定是否应将序列号和大小标题添加到每个消息中。这个可选属性默认为`false`。
`default-output-channel`
如果设置了此属性,则该属性提供了对通道的引用,如果通道解析无法返回任何通道,则应在该通道中发送消息。如果没有提供默认的输出通道,路由器将抛出一个异常。如果你想静默地删除这些消息,请将默认的输出通道属性值设置为`nullChannel`。
| |如果`resolution-required`是`false`且信道未解析,则仅向`default-output-channel`发送消息。|
|---|---------------------------------------------------------------------------------------------------------------------------|
`resolution-required`
此属性指定是否必须始终成功地将通道名解析为已存在的通道实例。如果设置为`true`,则在无法解析通道时会引发`MessagingException`。将此属性设置为`false`将导致忽略任何不可溶解的通道。这个可选属性默认为`true`。
| |如果指定,当`resolution-required`是`false`且信道未解析时,消息仅被发送到`default-output-channel`。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------|
`ignore-send-failures`
如果设置为`true`,则忽略发送到消息通道的失败。如果设置为`false`,则会抛出一个`MessageDeliveryException`,并且,如果路由器解析了多个通道,则后续的任何通道都不会接收到该消息。
此属性的确切行为取决于消息发送到的`Channel`的类型。例如,当使用直接通道(单线程)时,发送失败可能是由组件在更远的下游抛出的异常引起的。然而,当将消息发送到一个简单的队列通道(异步)时,引发异常的可能性非常小。
| |虽然大多数路由器会路由到单个通道,但它们可以返回多个通道名,例如,
`recipient-list-router`就是这样做的,
如果在只路由到单个通道的路由器上将此属性设置为`true`,则任何导致的异常都会被吞没,这通常没有什么意义。在这种情况下,最好是在流入口点的错误流中捕获异常。因此,当路由器实现返回多个通道名时,将`ignore-send-failures`属性设置为`true`通常更有意义,因为在发生故障的通道之后的其他通道仍将接收消息。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
此属性默认为`false`。
`timeout`
`timeout`属性指定向目标消息通道发送消息时等待的最大时间(以毫秒为单位)。默认情况下,发送操作会无限期地阻塞。
##### 顶层(在链外)
以下参数仅对链外的所有顶级路由器有效。
`id`
标识底层的 Spring Bean 定义,在路由器的情况下,它是`EventDrivenConsumer`或`PollingConsumer`的实例,这取决于路由器的`input-channel`分别是`SubscribableChannel`或`PollableChannel`。这是一个可选属性。
`auto-startup`
这个“生命周期”属性表示是否应该在应用程序上下文的启动期间启动这个组件。这个可选属性默认为`true`。
`input-channel`
此端点的接收消息通道。
`order`
此属性定义了当此端点作为订阅服务器连接到信道时调用的顺序。当该通道使用故障转移调度策略时,这一点尤其重要。当这个端点本身是具有队列的通道的轮询消费者时,它没有任何作用。
#### 路由器实现
由于基于内容的路由通常需要一些特定于领域的逻辑,因此大多数用例都需要 Spring 集成的选项,可以通过使用 XML 名称空间支持或注释来将任务委托给 POJO。这两个问题都将在后面讨论。然而,我们首先介绍了几个满足常见需求的实现。
##### `PayloadTypeRouter`
a`PayloadTypeRouter`将消息发送到由有效负载类型映射定义的通道,如下例所示:
```
```
Spring Integration(参见`[Namespace Support](./configuration.html#configuration-namespace)`)提供的命名空间也支持`PayloadTypeRouter`的配置,该命名空间通过将``配置及其相应的实现(通过使用``元素定义)合并为一个更简洁的配置元素,基本上简化了配置。下面的示例显示了一个`PayloadTypeRouter`配置,该配置与上面的配置等价,但使用了名称空间支持:
```
```
下面的示例展示了在 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 IntegrationFlows.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 IntegrationFlows.from("routingChannel")
.