From 7b920fa930a841006471bfb27de3606035554a00 Mon Sep 17 00:00:00 2001 From: 58148590088075383 <723423272@qq.com> Date: Mon, 7 Mar 2022 15:41:38 +0800 Subject: [PATCH] integration MT translated --- docs/spring-integration/amqp.md | 989 ++++++ docs/spring-integration/configuration.md | 700 ++++ docs/spring-integration/core.md | 1275 ++++++++ docs/spring-integration/dsl.md | 1061 +++++++ docs/spring-integration/endpoint-summary.md | 71 + docs/spring-integration/error-handling.md | 48 + docs/spring-integration/event.md | 141 + docs/spring-integration/feed.md | 102 + docs/spring-integration/file.md | 983 ++++++ docs/spring-integration/ftp.md | 1274 ++++++++ docs/spring-integration/gemfire.md | 214 ++ docs/spring-integration/history.md | 2271 +++++++++++++ docs/spring-integration/http.md | 800 +++++ docs/spring-integration/ip.md | 1422 +++++++++ docs/spring-integration/jdbc.md | 803 +++++ docs/spring-integration/jms.md | 623 ++++ docs/spring-integration/jmx.md | 246 ++ docs/spring-integration/jpa.md | 1002 ++++++ docs/spring-integration/kafka.md | 803 +++++ docs/spring-integration/kotlin-dsl.md | 72 + docs/spring-integration/mail.md | 464 +++ docs/spring-integration/message-publishing.md | 312 ++ docs/spring-integration/message-routing.md | 2106 ++++++++++++ .../message-transformation.md | 891 ++++++ docs/spring-integration/message.md | 196 ++ .../spring-integration/messaging-endpoints.md | 2818 +++++++++++++++++ docs/spring-integration/mongodb.md | 518 +++ docs/spring-integration/mqtt.md | 384 +++ docs/spring-integration/overview.md | 555 ++++ docs/spring-integration/preface.md | 50 + docs/spring-integration/r2dbc.md | 95 + docs/spring-integration/reactive-streams.md | 84 + docs/spring-integration/redis.md | 587 ++++ docs/spring-integration/resource.md | 63 + docs/spring-integration/resources.md | 5 + docs/spring-integration/rmi.md | 121 + docs/spring-integration/rsocket.md | 252 ++ docs/spring-integration/samples.md | 422 +++ docs/spring-integration/security.md | 119 + docs/spring-integration/sftp.md | 1144 +++++++ docs/spring-integration/spel.md | 150 + docs/spring-integration/stomp.md | 227 ++ docs/spring-integration/stream.md | 125 + docs/spring-integration/syslog.md | 126 + docs/spring-integration/system-management.md | 771 +++++ docs/spring-integration/testing.md | 295 ++ docs/spring-integration/transactions.md | 173 + docs/spring-integration/web-sockets.md | 320 ++ docs/spring-integration/webflux.md | 287 ++ docs/spring-integration/whats-new.md | 75 + docs/spring-integration/ws.md | 364 +++ docs/spring-integration/xml.md | 793 +++++ docs/spring-integration/xmpp.md | 329 ++ docs/spring-integration/zeromq.md | 150 + docs/spring-integration/zookeeper.md | 96 + 55 files changed, 30367 insertions(+) create mode 100644 docs/spring-integration/amqp.md create mode 100644 docs/spring-integration/configuration.md create mode 100644 docs/spring-integration/core.md create mode 100644 docs/spring-integration/dsl.md create mode 100644 docs/spring-integration/endpoint-summary.md create mode 100644 docs/spring-integration/error-handling.md create mode 100644 docs/spring-integration/event.md create mode 100644 docs/spring-integration/feed.md create mode 100644 docs/spring-integration/file.md create mode 100644 docs/spring-integration/ftp.md create mode 100644 docs/spring-integration/gemfire.md create mode 100644 docs/spring-integration/history.md create mode 100644 docs/spring-integration/http.md create mode 100644 docs/spring-integration/ip.md create mode 100644 docs/spring-integration/jdbc.md create mode 100644 docs/spring-integration/jms.md create mode 100644 docs/spring-integration/jmx.md create mode 100644 docs/spring-integration/jpa.md create mode 100644 docs/spring-integration/kafka.md create mode 100644 docs/spring-integration/kotlin-dsl.md create mode 100644 docs/spring-integration/mail.md create mode 100644 docs/spring-integration/message-publishing.md create mode 100644 docs/spring-integration/message-routing.md create mode 100644 docs/spring-integration/message-transformation.md create mode 100644 docs/spring-integration/message.md create mode 100644 docs/spring-integration/messaging-endpoints.md create mode 100644 docs/spring-integration/mongodb.md create mode 100644 docs/spring-integration/mqtt.md create mode 100644 docs/spring-integration/overview.md create mode 100644 docs/spring-integration/preface.md create mode 100644 docs/spring-integration/r2dbc.md create mode 100644 docs/spring-integration/reactive-streams.md create mode 100644 docs/spring-integration/redis.md create mode 100644 docs/spring-integration/resource.md create mode 100644 docs/spring-integration/resources.md create mode 100644 docs/spring-integration/rmi.md create mode 100644 docs/spring-integration/rsocket.md create mode 100644 docs/spring-integration/samples.md create mode 100644 docs/spring-integration/security.md create mode 100644 docs/spring-integration/sftp.md create mode 100644 docs/spring-integration/spel.md create mode 100644 docs/spring-integration/stomp.md create mode 100644 docs/spring-integration/stream.md create mode 100644 docs/spring-integration/syslog.md create mode 100644 docs/spring-integration/system-management.md create mode 100644 docs/spring-integration/testing.md create mode 100644 docs/spring-integration/transactions.md create mode 100644 docs/spring-integration/web-sockets.md create mode 100644 docs/spring-integration/webflux.md create mode 100644 docs/spring-integration/whats-new.md create mode 100644 docs/spring-integration/ws.md create mode 100644 docs/spring-integration/xml.md create mode 100644 docs/spring-integration/xmpp.md create mode 100644 docs/spring-integration/zeromq.md create mode 100644 docs/spring-integration/zookeeper.md diff --git a/docs/spring-integration/amqp.md b/docs/spring-integration/amqp.md new file mode 100644 index 0000000..e905c16 --- /dev/null +++ b/docs/spring-integration/amqp.md @@ -0,0 +1,989 @@ +# AMQP 支持 + +## [](#amqp)AMQP 支持 + +Spring 集成提供了用于通过使用高级消息队列协议接收和发送消息的通道适配器。 + +你需要在项目中包含此依赖项: + +Maven + +``` + + org.springframework.integration + spring-integration-amqp + 5.5.9 + +``` + +Gradle + +``` +compile "org.springframework.integration:spring-integration-amqp:5.5.9" +``` + +可提供以下适配器: + +* [入站通道适配器](#amqp-inbound-channel-adapter) + +* [入站网关](#amqp-inbound-gateway) + +* [出站通道适配器](#amqp-outbound-channel-adapter) + +* [出站网关](#amqp-outbound-gateway) + +* [异步出站网关](#amqp-async-outbound-gateway) + +Spring 集成还提供了由 AMQP 交换和队列支持的点对点消息通道和发布-订阅消息通道。 + +为了提供 AMQP 支持, Spring 集成依赖于([Spring AMQP](https://projects.spring.io/spring-amqp)),它将核心 Spring 概念应用于基于 AMQP 的消息传递解决方案的开发。 Spring AMQP 提供了与([Spring JMS](https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#jms))类似的语义。 + +Spring 鉴于所提供的 AMQP 通道适配器仅用于单向消息传递(发送或接收), Spring 集成还提供了用于请求-应答操作的入站和出站 AMQP 网关。 + +小贴士:你应该熟悉[reference documentation of the Spring AMQP project](https://docs.spring.io/spring-amqp/reference/html/)。它提供了关于 Spring 与 AMQP 的集成的更深入的信息,特别是与 RabbitMQ 的集成。 + +### [](#amqp-inbound-channel-adapter)入站通道适配器 + +下面的清单显示了 AMQP 入站通道适配器的可能配置选项: + +爪哇 DSL + +``` +@Bean +public IntegrationFlow amqpInbound(ConnectionFactory connectionFactory) { + return IntegrationFlows.from(Amqp.inboundAdapter(connectionFactory, "aName")) + .handle(m -> System.out.println(m.getPayload())) + .get(); +} +``` + +爪哇 + +``` +@Bean +public MessageChannel amqpInputChannel() { + return new DirectChannel(); +} + +@Bean +public AmqpInboundChannelAdapter inbound(SimpleMessageListenerContainer listenerContainer, + @Qualifier("amqpInputChannel") MessageChannel channel) { + AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(listenerContainer); + adapter.setOutputChannel(channel); + return adapter; +} + +@Bean +public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) { + SimpleMessageListenerContainer container = + new SimpleMessageListenerContainer(connectionFactory); + container.setQueueNames("aName"); + container.setConcurrentConsumers(2); + // ... + return container; +} + +@Bean +@ServiceActivator(inputChannel = "amqpInputChannel") +public MessageHandler handler() { + return new MessageHandler() { + + @Override + public void handleMessage(Message message) throws MessagingException { + System.out.println(message.getPayload()); + } + + }; +} +``` + +XML + +``` + (27) +``` + +|**1** |此适配器的唯一 ID。
可选的。| +|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2** |转换后的消息应发送到的消息通道。
需要。| +|**3** |AMQP 队列的名称(用逗号分隔的列表),应该从这些队列中消费消息。
required。| +|**4** |`MessageListenerContainer`的确认模式,
当设置为`MANUAL`时,传递标记和通道在消息头`amqp_deliveryTag`和`amqp_channel`中提供,分别。
用户应用程序负责确认。`NONE`表示没有确认(`autoAck`)。`AUTO`表示适配器的容器在下游流完成时进行确认。
可选(默认为自动)。
参见[入站端点确认模式](#amqp-inbound-ack)。| +|**5** |额外的 AOP 建议来处理与此入站通道适配器相关的横切行为。
可选的。| +|**6** |标志,指示该组件创建的通道是事务性的。
如果为真,它会告诉框架使用事务性通道,并根据结果以提交或回滚结束所有操作(发送或接收),但有一个异常,表示回滚。
可选(默认为假)。| +|**7** |指定要创建的并发消费者的数量。
默认值是`1`。
我们建议增加并发消费者的数量,以按比例分配来自队列的消息的消耗,
但是,请注意,一旦注册了多个消费者,任何订购保证都会丢失。,通常情况下,
,对于低容量队列使用一个使用者。
设置了“每个队列的使用者”时不允许使用。
可选。| +|**8** |Bean 参考 RabbitMQ.可选(默认为)。| +|**9** |错误消息应发送到的消息通道。
可选。| +|**10**|侦听器通道是否暴露于已注册的`ChannelAwareMessageListener`.
可选(默认为 true)。| +|**11**|在接收 AMQP 消息时使用的对`AmqpHeaderMapper`的引用。
可选的。
默认情况下,只有标准的 AMQP 属性(例如`contentType`)被复制到 Spring Integration`MessageHeaders`。
AMQP`MessageProperties`中的任何用户定义的标题都不会通过默认的`DefaultAmqpHeaderMapper`复制到消息中。如果提供了“request-header-names”,则不允许
。| +|**12**|要从 AMQP 请求映射到`MessageHeaders`中的 AMQP 头的名称的逗号分隔列表。
只能提供如果没有提供“header-mapper”引用。
此列表中的值也可以是与 header 名称匹配的简单模式(例如“\*”或“thing1\*,thing2”或“\*something”)。| +|**13**|引用`AbstractMessageListenerContainer`用于接收 AMQP 消息。
如果提供了此属性,则不应提供与侦听器容器配置相关的其他属性。
换句话说,通过设置此引用,你必须对侦听器容器配置承担全部责任。
唯一的例外是`MessageListener`本身。
因为这实际上是这个通道适配器实现的核心责任,所以引用的侦听器容器不能已经拥有自己的`MessageListener`。
可选的。| +|**14**|在接收 AMQP 消息时使用的`MessageConverter`。
可选。| +|**15**|在接收 AMQP 消息时使用的`MessagePropertiesConverter`。
可选。| +|**16**|指定底层`AbstractMessageListenerContainer`应该启动和停止的阶段,
启动顺序从最低到最高,关闭顺序与此相反,
默认情况下,该值为`Integer.MAX_VALUE`,这意味着这个容器开始得越晚,停止得越快。
可选的。| +|**17**|告诉 AMQP 代理在一个请求中要向每个使用者发送多少消息。
通常,你可以将这个值设置得很高,以提高吞吐量。
它应该大于或等于事务大小(请参见`tx-size`属性,
可选(默认为`1`)。| +|**18**|接收以毫秒为单位的超时。
可选(默认为`1000`)。| +|**19**|指定底层`AbstractMessageListenerContainer`的恢复尝试之间的间隔(以毫秒为单位)。
可选(默认为`5000`)。| +|**20**|如果“true”且代理上没有队列可用,则容器在启动时抛出一个致命的异常,如果在容器运行时删除了队列(在进行了三次被动声明队列的尝试之后),则停止。,容器不抛出异常并进入恢复模式,尝试根据`recovery-interval`重新启动。
可选(默认为`true`)。| +|**21**|在底层`AbstractMessageListenerContainer`停止之后和强制关闭 AMQP 连接之前等待工作人员的时间(以毫秒为单位)。
如果有工作人员在关闭信号出现时处于活动状态,只要他们能够在此超时范围内完成处理,他们就被允许完成处理。
否则,连接已关闭,消息仍未确认(如果通道是事务性的)。
可选(默认为`5000`)。| +|**22**|默认情况下,底层`AbstractMessageListenerContainer`使用`SimpleAsyncTaskExecutor`实现,该实现为每个任务启动一个新线程,并异步运行它,默认情况下,注意,此实现不重用线程。
考虑使用线程池`TaskExecutor`实现作为替代。
可选(默认为`SimpleAsyncTaskExecutor`)。| +|**23**|默认情况下,底层`AbstractMessageListenerContainer`创建了`DefaultTransactionAttribute`的新实例(它采用 EJB 方法回滚运行时,但不检查异常)。
可选(默认为`DefaultTransactionAttribute`)。| +|**24**|设置对底层`AbstractMessageListenerContainer`上的外部`PlatformTransactionManager`的引用。
事务管理器与`channel-transacted`属性一起工作。
如果在框架发送或接收消息时已经有事务在进行中并且`channelTransacted`标志是`true`,消息传递事务的提交或回滚将被推迟到当前事务结束时。
如果`channelTransacted`标志是`false`,则消息传递操作不应用事务语义(它是自动标记的)。
以获取更多信息,见[Transactions with Spring AMQP](https://docs.spring.io/spring-amqp/reference/html/%255Freference.html#%5Ftransactions)。
可选。| +|**25**|告诉`SimpleMessageListenerContainer`在单个事务中要处理多少消息(如果通道是事务性的)。
对于最佳结果,它应该小于或等于`prefetch-count`中设置的值。
当设置了“customers-per-queue”时不允许。
可选(默认为`1`)。| +|**26**|指示底层侦听器容器应该是`DirectMessageListenerContainer`,而不是默认的`SimpleMessageListenerContainer`。
有关更多信息,请参见[Spring AMQP Reference Manual](https://docs.spring.io/spring-amqp/reference/html/)。| +|**27**|当容器的`consumerBatchEnabled`是`true`时,确定适配器如何在消息有效负载中呈现一批消息。
当设置为`MESSAGES`(默认)时,有效负载是`List>`,其中每个消息都有从传入的 AMQP`Message`映射的头,并且有效负载是转换后的`body`。
当设置为`EXTRACT_PAYLOADS`时,有效负载是`List`,其中元素是从 AMQP`Message`体转换而来的。`EXTRACT_PAYLOADS_WITH_HEADERS`类似于`EXTRACT_PAYLOADS`,但是,除此之外,每个消息的头从`MessageProperties`映射到相应索引处的`List`;头名称为`AmqpInboundChannelAdapter.CONSOLIDATED_HEADERS`。| + +| |容器

注意,在使用 XML 配置外部容器时,不能使用 Spring AMQP 名称空间来定义容器。
这是因为名称空间至少需要一个``元素。,
在这种环境中,侦听器是适配器的内部。,
由于这个原因,你必须使用正常的 Spring ``定义来定义容器,如下例所示:

```
class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">




```| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |尽管 Spring 集成 JMS 和 AMQP 支持是相似的,但存在重要的差异。
JMS 入站通道适配器使用的是`JmsDestinationPollingSource`下的 covers,并且期望配置一个 poller。
AMQP 入站通道适配器使用的是`AbstractMessageListenerContainer`,并且是消息驱动的。,在这方面,
,它更类似于 JMS 消息驱动的通道适配器。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 5.5 开始,`AmqpInboundChannelAdapter`可以配置`org.springframework.amqp.rabbit.retry.MessageRecoverer`策略,当内部调用重试操作时,该策略在`RecoveryCallback`中使用。有关更多信息,请参见`setMessageRecoverer()`爪哇docs。 + +#### [](#amqp-debatching)批处理消息 + +有关批处理消息的更多信息,请参见[the Spring AMQP Documentation](https://docs.spring.io/spring-amqp/docs/current/reference/html/#template-batching)。 + +要生成具有 Spring 集成的批处理消息,只需使用`BatchingRabbitTemplate`配置出站端点。 + +默认情况下,接收批处理消息时,侦听器容器提取每个片段消息,适配器将为每个片段生成`Message`。从版本 5.2 开始,如果容器的`deBatchingEnabled`属性设置为`false`,则由适配器执行去批处理,并产生一个`Message>`,其有效负载是片段有效负载的列表(如果合适的话,在转换之后)。 + +默认的`BatchingStrategy`是`SimpleBatchingStrategy`,但这可以在适配器上被重写。 + +| |当重试操作需要恢复时,必须在批处理中使用`org.springframework.amqp.rabbit.retry.MessageBatchRecoverer`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------| + +### [](#polled-inbound-channel-adapter)已调查的入站通道适配器 + +#### [](#overview)概述 + +版本 5.0.1 引入了一个轮询通道适配器,允许你按需获取单个消息——例如,使用`MessageSourcePollingTemplate`或 Poller。有关更多信息,请参见[延迟确认可收集消息源](./polling-consumer.html#deferred-acks-message-source)。 + +它目前不支持 XML 配置。 + +下面的示例展示了如何配置`AmqpMessageSource`: + +爪哇 DSL + +``` +@Bean +public IntegrationFlow flow() { + return IntegrationFlows.from(Amqp.inboundPolledAdapter(connectionFactory(), DSL_QUEUE), + e -> e.poller(Pollers.fixedDelay(1_000)).autoStartup(false)) + .handle(p -> { + ... + }) + .get(); +} +``` + +爪哇 + +``` +@Bean +public AmqpMessageSource source(ConnectionFactory connectionFactory) { + return new AmqpMessageSource(connectionFactory, "someQueue"); +} +``` + +有关配置属性,请参见[爪哇doc](https://docs.spring.io/spring-integration/api/org/springframework/integration/amqp/inbound/AmqpMessageSource.html)。 + +XML + +``` +This adapter currently does not have XML configuration support. +``` + +#### [](#amqp-polled-debatching)批处理消息 + +见[批处理消息](#amqp-debatching)。 + +对于轮询的适配器,不存在侦听器容器,批处理的消息总是会被删除(如果`BatchingStrategy`支持这样做的话)。 + +### [](#amqp-inbound-gateway)入站网关 + +入站网关支持入站通道适配器上的所有属性(除了“通道”被“请求通道”代替),以及一些附加属性。下面的清单显示了可用的属性: + +Java DSL + +``` +@Bean // return the upper cased payload +public IntegrationFlow amqpInboundGateway(ConnectionFactory connectionFactory) { + return IntegrationFlows.from(Amqp.inboundGateway(connectionFactory, "foo")) + .transform(String.class, String::toUpperCase) + .get(); +} +``` + +Java + +``` +@Bean +public MessageChannel amqpInputChannel() { + return new DirectChannel(); +} + +@Bean +public AmqpInboundGateway inbound(SimpleMessageListenerContainer listenerContainer, + @Qualifier("amqpInputChannel") MessageChannel channel) { + AmqpInboundGateway gateway = new AmqpInboundGateway(listenerContainer); + gateway.setRequestChannel(channel); + gateway.setDefaultReplyTo("bar"); + return gateway; +} + +@Bean +public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory) { + SimpleMessageListenerContainer container = + new SimpleMessageListenerContainer(connectionFactory); + container.setQueueNames("foo"); + container.setConcurrentConsumers(2); + // ... + return container; +} + +@Bean +@ServiceActivator(inputChannel = "amqpInputChannel") +public MessageHandler handler() { + return new AbstractReplyProducingMessageHandler() { + + @Override + protected Object handleRequestMessage(Message requestMessage) { + return "reply to " + requestMessage.getPayload(); + } + + }; +} +``` + +XML + +``` + (9) +``` + +|**1**|此适配器的唯一 ID。
可选的。| +|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2**|将转换后的消息发送到的消息通道。
需要。| +|**3**|在接收 AMQP 消息时使用的对`AmqpHeaderMapper`的引用。
可选的。
默认情况下,只有标准的 AMQP 属性(例如`contentType`)才会被复制到 Spring Integration`MessageHeaders`。
AMQP`MessageProperties`中的任何用户定义的头文件都不会被默认的`DefaultAmqpHeaderMapper`复制到或复制到 AMQP 消息中。如果提供了“request-header-names”或“reply-header-names”,则不允许
。| +|**4**|要从 AMQP 请求映射到`MessageHeaders`的 AMQP 头的名称的逗号分隔列表。
只有在不提供“header-mapper”引用的情况下,才能提供此属性。
此列表中的值也可以是要与头名称匹配的简单模式(例如`"*"`或`"thing1*, thing2"`或`"*thing1"`)。| +|**5**|将`MessageHeaders`的名称以逗号分隔的列表映射到 AMQP 回复消息的 AMQP 消息属性中。
所有标准标题(例如`contentType`)都映射到 AMQP 消息属性,当用户定义的头被映射到’headers’属性时。
只有在不提供’header-mapper’引用的情况下才能提供此属性。
此列表中的值也可以是简单的模式,以便与头名称进行匹配(例如,`"*"`或`"foo*, bar"`或`"*foo"`)。| +|**6**|需要回复消息的消息通道。
可选。| +|**7**|设置底层`o.s.i.core.MessagingTemplate`上的`receiveTimeout`,用于接收来自回复通道的消息。
如果未指定,此属性默认为`1000`(1 秒)。
仅当容器线程在发送回复之前发送到另一个线程时才适用。| +|**8**|自定义的`AmqpTemplate` Bean 引用(对发送的回复消息有更多的控制)。
可以提供`RabbitTemplate`的替代实现。| +|**9**|`replyTo``o.s.amqp.core.Address`当`requestMessage`没有`replyTo`属性时使用的
如果未指定此选项,则不提供`amqp-template`属性,在请求消息中不存在`replyTo`属性,并抛出
an`IllegalStateException`,因为无法路由答复。
如果未指定此选项,并提供了外部`amqp-template`,没有引发异常。
你必须指定此选项,或者在该模板上配置默认的`exchange`和`routingKey`,
如果你预计请求消息中不存在`replyTo`属性的情况。| + +请参阅[入站通道适配器](#amqp-inbound-channel-adapter)中关于配置`listener-container`属性的注释。 + +从版本 5.5 开始,`AmqpInboundChannelAdapter`可以配置`org.springframework.amqp.rabbit.retry.MessageRecoverer`策略,当内部调用重试操作时,该策略在`RecoveryCallback`中使用。有关更多信息,请参见`setMessageRecoverer()`Javadocs。 + +#### [](#amqp-gateway-debatching)批处理消息 + +见[批处理消息](#amqp-debatching)。 + +### [](#amqp-inbound-ack)入站端点确认模式 + +默认情况下,入站端点使用`AUTO`确认模式,这意味着当下游集成流完成(或者通过使用`QueueChannel`或`ExecutorChannel`将消息传递给另一个线程)时,容器会自动确认消息。将模式设置为`NONE`将配置消费者,使得完全不使用确认(代理在消息发送后立即自动确认消息)。将模式设置为`MANUAL`,可以让用户代码在处理过程中的另一个点确认消息。为了支持这一点,在此模式下,端点在`amqp_channel`和`amqp_deliveryTag`标题中分别提供`Channel`和`deliveryTag`。 + +你可以在`Channel`上执行任何有效的 Rabbit 命令,但通常只使用`basicAck`和`basicNack`(或`basicReject`)。为了不干扰容器的操作,你不应该保留对通道的引用,并且仅在当前消息的上下文中使用它。 + +| |由于`Channel`是对“live”对象的引用,因此不能序列化它,并且如果消息被持久化,它就会丢失。| +|---|---------------------------------------------------------------------------------------------------------------------| + +下面的示例展示了如何使用`MANUAL`确认: + +``` +@ServiceActivator(inputChannel = "foo", outputChannel = "bar") +public Object handle(@Payload String payload, @Header(AmqpHeaders.CHANNEL) Channel channel, + @Header(AmqpHeaders.DELIVERY_TAG) Long deliveryTag) throws Exception { + + // Do some processing + + if (allOK) { + channel.basicAck(deliveryTag, false); + + // perhaps do some more processing + + } + else { + channel.basicNack(deliveryTag, false, true); + } + return someResultForDownStreamProcessing; +} +``` + +### [](#amqp-outbound-endpoints)出站端点 + +以下出站端点有许多类似的配置选项。从版本 5.2 开始,添加了`confirm-timeout`。通常,当启用了 Publisher Confirms 时,代理将快速返回一个 ACK(或 NACK),该 ACK 将被发送到相应的通道。如果在接收到确认之前关闭了通道,则 Spring AMQP 框架将合成 NACK。“丢失”ACK 永远不会发生,但是,如果你设置了此属性,则端点将定期检查它们,并在时间过去而未收到确认的情况下合成 NACK。 + +### [](#amqp-outbound-channel-adapter)出站通道适配器 + +下面的示例显示了 AMQP 出站通道适配器的可用属性: + +Java DSL + +``` +@Bean +public IntegrationFlow amqpOutbound(AmqpTemplate amqpTemplate, + MessageChannel amqpOutboundChannel) { + return IntegrationFlows.from(amqpOutboundChannel) + .handle(Amqp.outboundAdapter(amqpTemplate) + .routingKey("queue1")) // default exchange - route to queue 'queue1' + .get(); +} +``` + +Java + +``` +@Bean +@ServiceActivator(inputChannel = "amqpOutboundChannel") +public AmqpOutboundEndpoint amqpOutbound(AmqpTemplate amqpTemplate) { + AmqpOutboundEndpoint outbound = new AmqpOutboundEndpoint(amqpTemplate); + outbound.setRoutingKey("queue1"); // default exchange - route to queue 'queue1' + return outbound; +} + +@Bean +public MessageChannel amqpOutboundChannel() { + return new DirectChannel(); +} +``` + +XML + +``` + (20) +``` + +|**1** |此适配器的唯一 ID。
可选。| +|------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2** |消息通道,其中的消息应该被发送到有他们转换和发布到一个 AMQP 交换。
需要。| +|**3** |Bean 参考配置的 AMQP 模板.
可选(默认为`amqpTemplate`)。| +|**4** |将消息发送到的 AMQP 交换的名称。
如果不提供,则将消息发送到默认的无名称交换。
与“exchange-name-expression”互斥。
可选。| +|**5** |一种 SPEL 表达式,用于确定将消息作为根对象发送到的 AMQP 交换的名称。
如果不提供,则将消息发送到默认的无名称交换。
与“exchange-name”互斥。
可选。| +|**6** |当注册了多个使用者时,此使用者的顺序,从而启用负载平衡和故障转移。
可选(默认为`Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`)。| +|**7** |发送消息时使用的固定路由键。
默认情况下,这是一个空的`String`。
与“routing-key-expression”互斥。
可选。| +|**8** |一种 SPEL 表达式,用于确定发送消息时要使用的路由密钥,并将消息作为根对象(例如,“payload.key”)。
默认情况下,这是一个空的`String`。
与“routing-key”互斥。
可选。| +|**9** |消息的默认传递模式:`PERSISTENT`或`NON_PERSISTENT`。
如果`header-mapper`设置了传递模式,则重写
如果存在 Spring 集成消息头`amqp_deliveryMode`,`DefaultHeaderMapper`设置该值。
如果没有提供此属性,而 header mapper 没有设置该属性,则默认值取决于`RabbitTemplate`所使用的底层 Spring amqp`MessagePropertiesConverter`。
如果根本没有进行自定义,则默认值是`PERSISTENT`。
可选的。| +|**10**|定义相关数据的表达式。
当提供时,这将配置底层 AMQP 模板以接收发布者确认。
需要专用的`RabbitTemplate`和`CachingConnectionFactory`,其`publisherConfirms`属性设置为`true`,当接收到发布者确认并提供相关数据时,
,它被写入`confirm-ack-channel`或`confirm-nack-channel`,这取决于确认类型。
确认的有效负载是相关数据,正如这个表达式所定义的。
消息有一个’amqp\_publishconfirm’头,它被设置为`true`(`ack`)或`false`(`nack`)。
示例:`headers['myCorrelationData']`而`payload`.
版本 4.1 引入了`amqp_publishConfirmNackCause`消息头。
它包含用于发布商确认的“nack”的`cause`。
从版本 4.2 开始,如果表达式解析为`Message`实例(例如`#this`),在`ack`/`nack`通道上发出的消息是基于该消息,并添加了附加的报头。
以前,无论类型如何,都以相关数据作为其有效负载来创建新的消息。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选。| +|**11**|positive(`ack`)publisher 确认发送到的通道。
有效负载是由`confirm-correlation-expression`定义的相关数据。
如果表达式是`#root`或`#this`,则消息是从原始消息生成的,将`amqp_publishConfirm`标头设置为`true`。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选(默认值为`nullChannel`)。| +|**12**|负(`nack`)发布者确认发送到的通道。
有效负载是由`confirm-correlation-expression`定义的相关数据(如果没有`ErrorMessageStrategy`配置)。
如果表达式是`#root`或`#this`,则消息是从原始消息构建的,将`amqp_publishConfirm`标头设置为`false`。
当存在`ErrorMessageStrategy`时,消息是带有`ErrorMessage`有效载荷的
消息。[发布服务器确认和返回的替代机制](#alternative-confirms-returns)还请参见
可选(默认值为`nullChannel`)。| +|**13**|设置好后,如果在毫秒内没有收到发布者确认,则适配器将合成一个否定确认。
Pending 确认每检查 50% 的值,因此,一个 NACK 的实际发送时间将在这个值的 1 倍到 1.5 倍之间。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
默认为零(不会生成 NACK)。| +|**14**|当设置为 true 时,调用线程将阻塞,等待发布者确认。
这需要为确认和`confirm-correlation-expression`配置一个`MessageHandlingException`。
线程将阻塞长达`confirm-timeout`(默认情况下为 5 秒)。如果发生超时,
,将抛出`MessageTimeoutException`。
如果启用返回并返回消息,或者在等待确认时发生任何其他异常,将抛出`MessageHandlingException`,并附带相应的消息。| +|**15**|返回的消息被发送到的通道。
当提供时,底层的 AMQP 模板被配置为将不可交付的消息返回给适配器。
当没有`ErrorMessageStrategy`配置时,消息是根据从 AMQP 接收到的数据构造的,具有以下附加头:`amqp_returnReplyCode`,`amqp_returnReplyText`,`amqp_returnExchange`,`amqp_returnRoutingKey`。
当存在`ErrorMessageStrategy`时,消息是带有`ReturnedAmqpMessageException`有效载荷的`ErrorMessage`。
另请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选。| +|**16**|对`ErrorMessageStrategy`实现的引用,用于在发送返回的或负面确认的消息时构建`ErrorMessage`实例。| +|**17**|在默认情况下,只有标准的 AMQP 属性(例如`contentType`)被复制到 Spring 集成`MessageHeaders`。
任何用户定义的标题都不会通过默认的`DefaultAmqpHeaderMapper`复制到消息。
如果提供’请求-header-names’,则不允许
可选的。| +|**18**|如果提供了“header-mapper”引用,则不允许
将 AMQP 头的名称从`MessageHeaders`映射到 AMQP 消息的逗号分隔的列表。
此列表中的值也可以是与头名称匹配的简单模式(例如`"*"`或`"thing1*, thing2"`或`"*thing1"`)。| +|**19**|当设置为`false`时,端点将尝试在应用程序上下文初始化期间连接到代理。,
这允许“快速失败”地检测错误的配置,但如果代理关闭,也会导致初始化失败,
当`true`(默认值)时,当发送第一条消息时,连接被建立(如果它不存在,因为其他组件已经建立了它)。| +|**20**|当设置为`true`时,类型为`Iterable>`的有效载荷将在单个`RabbitTemplate`调用的范围内作为离散消息在同一通道上发送。
需要`RabbitTemplate`。
时`wait-for-confirms`为真,`RabbitTemplate.waitForConfirmsOrDie()`是在消息发送后调用的。
使用事务模板,发送将在新事务或已经启动的事务(如果存在)中执行。| + +| |return-channel

使用`return-channel`需要一个`RabbitTemplate`,其`mandatory`属性设置为`true`,而`CachingConnectionFactory`属性设置为`true`。
当使用带有返回的多个出站端点时,每个端点都需要一个单独的`RabbitTemplate`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### [](#amqp-outbound-gateway)出站网关 + +下面的清单显示了 AMQP 出站网关的可能属性: + +Java DSL + +``` +@Bean +public IntegrationFlow amqpOutbound(AmqpTemplate amqpTemplate) { + return f -> f.handle(Amqp.outboundGateway(amqpTemplate) + .routingKey("foo")) // default exchange - route to queue 'foo' + .get(); +} + +@MessagingGateway(defaultRequestChannel = "amqpOutbound.input") +public interface MyGateway { + + String sendToRabbit(String data); + +} +``` + +Java + +``` +@Bean +@ServiceActivator(inputChannel = "amqpOutboundChannel") +public AmqpOutboundEndpoint amqpOutbound(AmqpTemplate amqpTemplate) { + AmqpOutboundEndpoint outbound = new AmqpOutboundEndpoint(amqpTemplate); + outbound.setExpectReply(true); + outbound.setRoutingKey("foo"); // default exchange - route to queue 'foo' + return outbound; +} + +@Bean +public MessageChannel amqpOutboundChannel() { + return new DirectChannel(); +} + +@MessagingGateway(defaultRequestChannel = "amqpOutboundChannel") +public interface MyGateway { + + String sendToRabbit(String data); + +} +``` + +XML + +``` + (19) +``` + +|**1** |此适配器的唯一 ID。
可选。| +|------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2** |将消息发送到的消息通道将其转换并发布到 AMQP Exchange。
required。| +|**3** |Bean 参考配置的 AMQP 模板.
可选(默认为`amqpTemplate`)。| +|**4** |将消息发送到的 AMQP Exchange 的名称。
如果不提供,则将消息发送到默认的 CXChange。
与“exchange-name-expression”互斥。
可选。| +|**5** |一种 SPEL 表达式,用于确定 AMQP 交换的名称,将消息作为根对象发送到该消息。
如果不提供,则将消息发送到默认的无名称交换。
与“exchange-name”互斥。
可选。| +|**6** |当注册了多个使用者时,此使用者的顺序,从而启用负载平衡和故障转移。
可选(默认为`Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`)。| +|**7** |从 AMQP 队列接收并转换后,应将回复发送到的消息通道。
可选。| +|**8** |网关在将回复消息发送到`reply-channel`时等待的时间。
只有当`reply-channel`可以阻塞时,才适用于此。例如,容量限制为当前已满的`QueueChannel`。
默认值为无穷大。| +|**9** |当`true`时,如果在`AmqpTemplate’s `replyTimeout` property.
Defaults to `true` 内没有收到回复消息,网关将抛出一个异常。| +|**10**|在发送消息时使用的`routing-key`。
默认情况下,这是一个空的`String`。
与’routing-key-expression’互斥。
可选。| +|**11**|一种 SPEL 表达式,用于确定发送消息时要使用的`routing-key`,并将消息作为根对象(例如,“payload.key”)。
默认情况下,这是一个空的`String`。
与“routing-key”互斥。
可选的。| +|**12**|消息的默认传递模式:`PERSISTENT`或`NON_PERSISTENT`。
如果`header-mapper`设置了传递模式,则重写
如果存在 Spring 集成消息头`amqp_deliveryMode`,`DefaultHeaderMapper`设置该值。
如果没有提供此属性,而 header mapper 没有设置该属性,则默认值取决于`RabbitTemplate`所使用的底层 Spring amqp`MessagePropertiesConverter`。
如果根本没有进行自定义,则默认值是`PERSISTENT`。
可选的。| +|**13**|由于版本 4.2.
定义相关数据的表达式。
当提供时,这将配置底层 AMQP 模板以接收发布者确认。
需要一个专用的`RabbitTemplate`和一个`CachingConnectionFactory`,将`publisherConfirms`属性设置为`true`,当接收到发布者确认并提供相关数据时,
,它被写入`confirm-ack-channel`或`confirm-nack-channel`,这取决于确认类型。
确认的有效负载是相关数据,正如这个表达式所定义的。
消息的头’amqp\_publishconfirm’设置为`true`(`ack`)或`false`(`nack`)。
用于`nack`确认, Spring 集成提供了一个额外的头`amqp_publishConfirmNackCause`。
示例:`headers['myCorrelationData']`和`payload`。
如果表达式解析为`Message`实例(例如`#this`),则在
通道上发出的消息
是基于该消息的,并添加了额外的头`nack`。无论类型如何,都将创建一个新消息,并将相关数据作为其有效负载。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选。| +|**14**|正的(`ack`)发布者确认发送到的通道。
有效负载是由`confirm-correlation-expression`定义的相关数据。
如果表达式是`#root`或`#this`,则消息是根据原始消息构建的,将`amqp_publishConfirm`标头设置为`true`。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选(默认值为`nullChannel`)。| +|**15**|负(`nack`)发布者确认发送到的通道。
有效负载是由`confirm-correlation-expression`定义的相关数据(如果没有`ErrorMessageStrategy`配置)。
如果表达式是`#root`或`#this`,则消息是根据原始消息构建的,当`amqp_publishConfirm`标头设置为`false`.
时,当存在`ErrorMessageStrategy`时,消息是带有`NackedAmqpMessageException`有效载荷的
消息。[发布服务器确认和返回的替代机制](#alternative-confirms-returns)另请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选(默认值为`nullChannel`)。| +|**16**|设置好后,如果在毫秒内没有收到发布者确认,网关将合成一个否定确认,
Pending 确认每检查 50% 的值,因此,一个 NACK 的实际发送时间将在这个值的 1 倍到 1.5 倍之间。
默认为零(不会生成 NACK)。| +|**17**|返回的消息被发送到的通道。
当提供时,底层的 AMQP 模板被配置为将不可交付的消息返回给适配器。
当没有`ErrorMessageStrategy`配置时,消息是根据从 AMQP 接收到的数据构造的,具有以下附加头:`amqp_returnReplyCode`,`amqp_returnReplyText`,`amqp_returnExchange`,和`amqp_returnRoutingKey`。
当存在`ErrorMessageStrategy`时,消息是带有`ErrorMessage`有效载荷的
。也参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选的。| +|**18**|对`ErrorMessageStrategy`实现的引用,用于在发送返回的或负面确认的消息时构建`ErrorMessage`实例。| +|**19**|当设置为`false`时,端点将尝试在应用程序上下文初始化期间连接到代理。,
如果代理停机,通过记录错误消息,可以“快速失败”地检测错误配置,
当`true`(默认值),当发送第一条消息时,连接被建立(如果它不存在,因为其他组件已经建立了它)。| + +| |return-channel

使用`return-channel`需要一个`RabbitTemplate`,其`mandatory`属性设置为`true`,而`CachingConnectionFactory`属性设置为`true`。
当使用多个带返回的出站端点时,每个端点都需要一个单独的`RabbitTemplate`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |底层的`AmqpTemplate`具有五秒的默认`replyTimeout`。
如果你需要更长的超时,则必须在`template`上配置它。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------| + +请注意,出站适配器和出站网关配置之间的唯一区别是`expectReply`属性的设置。 + +### [](#amqp-async-outbound-gateway)异步出站网关 + +在上一节中讨论的网关是同步的,因为发送线程被挂起,直到收到答复(或发生超时)。 Spring 集成版本 4.3 增加了一个异步网关,它使用 Spring AMQP 中的`AsyncRabbitTemplate`。当发送消息时,线程在发送操作完成后立即返回,当收到消息时,响应将在模板的侦听器容器线程上发送。当在 Poller 线程上调用网关时,这可能是有用的。该线程已被释放,并可用于框架中的其他任务。 + +下面的清单显示了 AMQP 异步出站网关的可能配置选项: + +Java DSL + +``` +@Configuration +public class AmqpAsyncApplication { + + @Bean + public IntegrationFlow asyncAmqpOutbound(AsyncRabbitTemplate asyncRabbitTemplate) { + return f -> f + .handle(Amqp.asyncOutboundGateway(asyncRabbitTemplate) + .routingKey("queue1")); // default exchange - route to queue 'queue1' + } + + @MessagingGateway(defaultRequestChannel = "asyncAmqpOutbound.input") + public interface MyGateway { + + String sendToRabbit(String data); + + } + +} +``` + +Java + +``` +@Configuration +public class AmqpAsyncConfig { + + @Bean + @ServiceActivator(inputChannel = "amqpOutboundChannel") + public AsyncAmqpOutboundGateway amqpOutbound(AsyncRabbitTemplate asyncTemplate) { + AsyncAmqpOutboundGateway outbound = new AsyncAmqpOutboundGateway(asyncTemplate); + outbound.setRoutingKey("foo"); // default exchange - route to queue 'foo' + return outbound; + } + + @Bean + public AsyncRabbitTemplate asyncTemplate(RabbitTemplate rabbitTemplate, + SimpleMessageListenerContainer replyContainer) { + + return new AsyncRabbitTemplate(rabbitTemplate, replyContainer); + } + + @Bean + public SimpleMessageListenerContainer replyContainer() { + SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(ccf); + container.setQueueNames("asyncRQ1"); + return container; + } + + @Bean + public MessageChannel amqpOutboundChannel() { + return new DirectChannel(); + } + +} +``` + +XML + +``` + (18) +``` + +|**1** |此适配器的唯一 ID。
可选的。| +|------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2** |消息通道中的消息应该被发送到,以便将它们转换并发布到 AMQP 交换。
需要。| +|**3** |Bean 参考配置的`AsyncRabbitTemplate`.
可选的(它默认为`asyncRabbitTemplate`)。| +|**4** |将消息发送到哪个 AMQP Exchange 的名称。
如果不提供,则将消息发送到缺省的、无名称的 Exchange。
与“exchange-name-expression”互斥。
可选。| +|**5** |一种 SPEL 表达式,用于确定将消息作为根对象发送到的 AMQP 交换的名称。
如果不提供,则将消息发送到默认的无名称交换。
与“exchange-name”互斥。
可选。| +|**6** |当注册了多个使用者时,此使用者的顺序,从而启用负载平衡和故障转移。
可选(它默认为`Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]`)。| +|**7** |从 AMQP 队列接收并转换后,应将回复发送到的消息通道。
可选。| +|**8** |网关在将回复消息发送到`reply-channel`时等待的时间。
只有当`reply-channel`可以阻塞时,才适用于此,例如容量限制为当前已满的`QueueChannel`。
默认值为无穷大。| +|**9** |当`AsyncRabbitTemplate’s `receiveTimeout` property and this setting is `true`, the gateway sends an error message to the inbound message’s `errorchannel` header.
When no reply message is received within the `AsyncrabbitTemplate 的`receiveTimeout`属性中没有接收到回复消息,并且此设置为`false`,网关将向默认的`errorChannel`发送一条错误消息。
默认为`true`。| +|**10**|发送消息时使用的路由键。
默认情况下,这是一个空的`String`。
与“routing-key-expression”互斥。
可选。| +|**11**|一种 SPEL 表达式,用于确定发送消息时使用的路由键,
将消息作为根对象(例如,“payload.key”)。
默认情况下,这是一个空的`String`。
与“routing-key”互斥。
可选。| +|**12**|消息的默认传递模式:`PERSISTENT`或`NON_PERSISTENT`。
如果`header-mapper`设置了传递模式,则重写
如果存在 Spring 集成消息头(`amqp_deliveryMode`),`DefaultHeaderMapper`设置该值。
如果未提供此属性,而 header mapper 未对其进行设置,则默认值取决于`RabbitTemplate`所使用的底层 Spring amqp`MessagePropertiesConverter`。
如果未进行自定义,则默认值为`PERSISTENT`。
可选。| +|**13**|定义相关数据的表达式。
当提供时,这将配置底层 AMQP 模板以接收发布者确认。
需要专用的`RabbitTemplate`和`CachingConnectionFactory`,其`publisherConfirms`属性设置为`true`。
当接收到发布者确认并提供相关数据时,确认被写入`confirm-ack-channel`或`confirm-nack-channel`,这取决于确认类型。
确认的有效载荷是由该表达式定义的相关数据,并且消息的“AMQP\_PublishConference”头文件设置为`true`(`ack`)或`false`(`nack`)。
对于`nack`实例,提供了一个额外的头文件。
示例:,`headers['myCorrelationData']`如果表达式,则将这个实例分解为“<>,如”gt=“658”。在`ack`/`nack`通道上发出的消息是基于该消息,并添加了附加的头。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选。| +|**14**|正的(`ack`)发布者确认被发送到的通道。
有效负载是由`confirm-correlation-expression`定义的相关数据。
要求底层`enableConfirms`将其`enableConfirms`属性设置为
还请参见
<676"/>r=”可选的(默认值是“gt r=”gt r=“<677"/>)。| +|**15**|由于版本 4.2.
向其发送阴性(`nack`)发布者确认信息的通道。
有效载荷是由`confirm-correlation-expression`定义的相关数据。`AsyncRabbitTemplate`要求底层`enableConfirms`将其`enableConfirms`属性设置为
。还请参见<692"/>r=“/>r=”693"/>(默认值是可选的。| +|**16**|设置好后,如果在毫秒内没有收到发布者确认,网关将合成一个否定确认,
Pending 确认每检查 50% 的值,因此,一个 NACK 的实际发送时间将在这个值的 1 倍到 1.5 倍之间。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
默认为零(不会生成 NACK)。| +|**17**|返回的消息被发送到的通道。
当提供时,底层的 AMQP 模板被配置为将不可交付的消息返回到网关。
消息是根据从 AMQP 接收的数据构造的,具有以下附加头:`amqp_returnReplyCode`,`amqp_returnReplyText`,`amqp_returnExchange`,而`amqp_returnRoutingKey`.
要求底层`AsyncRabbitTemplate`将其`mandatory`属性设置为`true`。
还请参见[发布服务器确认和返回的替代机制](#alternative-confirms-returns)。
可选。| +|**18**|当设置为`false`时,端点将在应用程序上下文初始化期间尝试连接到代理,
这样做可以通过记录代理停机时的错误消息来“快速失败”地检测错误配置,
当`true`(默认值)时,在发送第一条消息时建立连接(如果它不存在,因为其他组件建立了
它)。| + +有关更多信息,请参见[异步服务激活器](./service-activator.html#async-service-activator)。 + +| |RabbitTemplate

当你使用确认和返回时,我们建议将连接到`RabbitTemplate`的`AsyncRabbitTemplate`中的
专用。否则,可能会遇到意想不到的副作用。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### [](#alternative-confirms-returns)发布者确认和返回的替代机制 + +当连接工厂被配置为 Publisher 确认和返回时,上面的小节讨论了用于异步接收确认和返回的消息通道的配置。从版本 5.4 开始,有一个额外的机制,通常更容易使用。 + +在这种情况下,不要配置`confirm-correlation-expression`或确认和返回通道。相反,在`AmqpHeaders.PUBLISH_CONFIRM_CORRELATION`头中添加一个`CorrelationData`实例;然后,你可以通过在已发送消息的`CorrelationData`实例中检查将来的状态来等待结果。在将来完成之前,将始终填充`returnedMessage`字段(如果返回了消息)。 + +``` +CorrelationData corr = new CorrelationData("someId"); // <--- Unique "id" is required for returns +someFlow.getInputChannel().send(MessageBuilder.withPayload("test") + .setHeader("rk", "someKeyThatWontRoute") + .setHeader(AmqpHeaders.PUBLISH_CONFIRM_CORRELATION, corr) + .build()); +... +try { + Confirm Confirm = corr.getFuture().get(10, TimeUnit.SECONDS); + Message returned = corr.getReturnedMessage(); + if (returned !- null) { + // message could not be routed + } +} +catch { ... } +``` + +为了提高性能,你可能希望发送多条消息,然后等待确认,而不是一次发送一条消息。返回的消息是转换后的原始消息;你可以使用所需的任何附加数据进行子类`CorrelationData`。 + +### [](#amqp-conversion-inbound)入站消息转换 + +到达通道适配器或网关的入站消息将使用消息转换器转换为`spring-messaging``Message`有效负载。默认情况下,使用`SimpleMessageConverter`,它处理 Java 序列化和文本。默认情况下,标头使用`DefaultHeaderMapper.inboundMapper()`进行映射。如果发生了转换错误,并且没有定义错误通道,那么异常将被抛到容器中,并由侦听器容器的错误处理程序处理。默认的错误处理程序将转换错误视为致命错误,并且消息将被拒绝(如果队列是这样配置的,则将其路由到死信交换)。如果定义了错误通道,则`ErrorMessage`有效负载是一个`ListenerExecutionFailedException`,带有属性`failedMessage`(无法转换的 Spring AMQP 消息)和`cause`。如果容器`AcknowledgeMode`是`AUTO`(默认值),并且错误流在不抛出异常的情况下消耗错误,则将确认原始消息。如果错误流抛出一个异常,那么异常类型将与容器的错误处理程序一起,决定是否重新请求消息。如果容器配置为`AcknowledgeMode.MANUAL`,则有效负载是带有附加属性`channel`和`deliveryTag`的`ManualAckListenerExecutionFailedException`。这使得错误流能够为消息调用`basicAck`或`basicNack`(或`basicReject`)来控制其配置。 + +### [](#content-type-conversion-outbound)出站消息转换 + +Spring AMQP1.4 引入了`ContentTypeDelegatingMessageConverter`,其中实际的转换器是基于传入的内容类型选择消息属性的。这可以由入站端点使用。 + +从 Spring 集成版本 4.3 开始,你也可以在出站端点上使用`ContentTypeDelegatingMessageConverter`,并使用`contentType`头指定使用哪个转换器。 + +下面的示例配置了`ContentTypeDelegatingMessageConverter`,默认的转换器是`SimpleMessageConverter`(用于处理 Java 序列化和纯文本),以及一个 JSON 转换器: + +``` + + + + + + + + + + + + + + + +``` + +将消息发送到`ctRequestChannel`,并将`contentType`头设置为`application/json`,将导致 JSON 转换器被选中。 + +这适用于出站通道适配器和网关。 + +| |从版本 5.0 开始,添加到出站消息的`MessageProperties`的标题永远不会被映射的标题覆盖(默认情况下)。
以前,只有当消息转换器是`ContentTypeDelegatingMessageConverter`(在这种情况下,头是首先映射的,以便可以选择适当的转换器)。,对于其他转换器,例如`SimpleMessageConverter`,映射的标头覆盖了转换器添加的任何标头。
当出站消息有一些剩余的`contentType`标头(可能来自入站通道适配器)时,这会导致问题。并且正确的出站`contentType`被错误地覆盖。
解决方法是在将消息发送到出站端点之前使用头部过滤器删除头部。

但是,在某些情况下,需要使用以前的行为—例如,当`String`包含 JSON 的有效负载时,`SimpleMessageConverter`不知道内容,并将`contentType`消息属性设置为`text/plain`,但你的应用程序想要重写通过将发送到出站端点的消息的`contentType`头设置为`application/json`。
`ObjectToJsonTransformer`确实做到了这(默认情况下)。

现在在出站通道适配器和网关上(以及在 AMQP 支持的通道上)有一个名为`headersMappedLast`的属性。
将其设置为`true`,以恢复覆盖转换器添加的属性的行为。

从 5.1.9 版本开始,类似的`replyHeadersMappedLast`是为`AmqpInboundGateway`提供的,当我们生成一个答复并想要覆盖由转换器填充的标题时。
有关更多信息,请参见其 Javadocs。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### [](#amqp-user-id)出站用户 ID + +Spring AMQP 版本 1.6 引入了一种机制,以允许用于出站消息的默认用户 ID 的规范。始终可以设置`AmqpHeaders.USER_ID`标头,它现在优先于默认值。这对消息接收者可能很有用。对于入站消息,如果消息发布者设置了属性,则该属性在`AmqpHeaders.RECEIVED_USER_ID`标头中可用。注意 RabbitMQ[验证用户 ID 是连接的实际用户 ID,或者该连接允许模拟](https://www.rabbitmq.com/validated-user-id.html)。 + +要为出站消息配置默认的用户 ID,请在`RabbitTemplate`上对其进行配置,并将出站适配器或网关配置为使用该模板。类似地,要在回复中设置用户 ID 属性,请在入站网关中插入一个适当配置的模板。有关更多信息,请参见[Spring AMQP documentation](https://docs.spring.io/spring-amqp/reference/html/_reference.html#template-user-id)。 + +### [](#amqp-delay)延迟消息交换 + +Spring AMQP 支持[RabbitMQ 延迟消息交换插件](https://docs.spring.io/spring-amqp/reference/html/#delayed-message-exchange)。对于入站消息,`x-delay`标头映射到`AmqpHeaders.RECEIVED_DELAY`标头。设置`AMQPHeaders.DELAY`报头会在出站消息中设置相应的`x-delay`报头。你还可以在出站端点上指定`delay`和`delayExpression`属性(当使用 XML 配置时,`delay-expression`)。这些属性优先于`AmqpHeaders.DELAY`标头。 + +### [](#amqp-channels)AMQP 支持的消息通道 + +有两个消息通道实现可用。一种是点对点,另一种是发布订阅。这两个通道都为底层`AmqpTemplate`和`SimpleMessageListenerContainer`提供了广泛的配置属性(如本章前面所示的通道适配器和网关)。然而,我们在这里展示的示例具有最小的配置。探索 XML 模式以查看可用的属性。 + +点对点通道可能看起来像以下示例: + +``` + +``` + +在封面下,前面的示例将导致声明一个名为`Queue`的`si.p2pChannel`,并将此通道发送到该`Queue`(从技术上讲,通过发送到与该`Queue`的名称匹配的路由密钥直接交换)。此通道还在`Queue`上注册消费者。如果你希望通道是“pollable”而不是消息驱动的,请提供值为`message-driven`的`false`的`message-driven`标志,如下例所示: + +``` + +``` + +发布-订阅频道可能如下所示: + +``` + +``` + +在封面下,前面的示例将导致声明一个名为`si.fanout.pubSubChannel`的扇出交换,并且此通道将发送到该扇出交换。该通道还声明了一个名为独占的、自动删除的非持久性`Queue`的服务器,并将其绑定到 FanOut Exchange,同时在该`Queue`上注册一个消费者以接收消息。对于发布-订阅-通道,没有“pollable”选项。它必须是消息驱动的。 + +从版本 4.1 开始,AMQP 支持的消息通道(与`channel-transacted`结合使用)支持`template-channel-transacted`,以分离`transactional`的`transactional`配置和`RabbitTemplate`。请注意,以前,`channel-transacted`默认为`true`。现在,默认情况下,对于`AbstractMessageListenerContainer`,它是`false`。 + +在版本 4.3 之前,AMQP 支持的通道只支持带有`Serializable`有效负载和报头的消息。整个消息被转换(序列化)并发送到 RabbitMQ。现在,你可以将`extract-payload`属性(或者在使用 Java 配置时`setExtractPayload()`)设置为`true`。当此标志`true`时,将以与使用通道适配器类似的方式转换消息有效负载并映射消息头。这种安排使 AMQP 支持的通道可用于不可序列化的有效负载(可能与另一个消息转换器一起使用,例如`Jackson2JsonMessageConverter`)。有关默认映射标头的更多信息,请参见[AMQP 消息头](#amqp-message-headers)。你可以通过提供使用`outbound-header-mapper`和`inbound-header-mapper`属性的定制映射器来修改映射。你现在还可以指定`default-delivery-mode`,它用于在没有`amqp_deliveryMode`报头时设置交付模式。默认情况下, Spring AMQP`MessageProperties`使用`PERSISTENT`交付模式。 + +| |与其他支持持久性的通道一样,支持 AMQP 的通道旨在提供消息持久性,以避免消息丢失。
它们不打算将工作分发给其他对等应用程序。
为此,请使用通道适配器。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |从版本 5.0 开始,poller 通道现在为指定的`receiveTimeout`阻塞 poller 线程(默认值为 1 秒)。
以前,与其他`PollableChannel`实现不同,如果没有可用的消息,线程将立即返回到调度程序,与接收超时无关。
拦截比使用`basicGet()`检索消息(没有超时)要贵一些,因为必须创建一个使用者来接收每个消息。
要恢复以前的行为,请将 poller 的`receiveTimeout`设置为 0。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#configuring-with-java-configuration)使用 Java 配置进行配置 + +下面的示例展示了如何使用 Java 配置来配置通道: + +``` +@Bean +public AmqpChannelFactoryBean pollable(ConnectionFactory connectionFactory) { + AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean(); + factoryBean.setConnectionFactory(connectionFactory); + factoryBean.setQueueName("foo"); + factoryBean.setPubSub(false); + return factoryBean; +} + +@Bean +public AmqpChannelFactoryBean messageDriven(ConnectionFactory connectionFactory) { + AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean(true); + factoryBean.setConnectionFactory(connectionFactory); + factoryBean.setQueueName("bar"); + factoryBean.setPubSub(false); + return factoryBean; +} + +@Bean +public AmqpChannelFactoryBean pubSub(ConnectionFactory connectionFactory) { + AmqpChannelFactoryBean factoryBean = new AmqpChannelFactoryBean(true); + factoryBean.setConnectionFactory(connectionFactory); + factoryBean.setQueueName("baz"); + factoryBean.setPubSub(false); + return factoryBean; +} +``` + +#### [](#configuring-with-the-java-dsl)使用 Java DSL 进行配置 + +下面的示例展示了如何使用 Java DSL 配置通道: + +``` +@Bean +public IntegrationFlow pollableInFlow(ConnectionFactory connectionFactory) { + return IntegrationFlows.from(...) + ... + .channel(Amqp.pollableChannel(connectionFactory) + .queueName("foo")) + ... + .get(); +} + +@Bean +public IntegrationFlow messageDrivenInFow(ConnectionFactory connectionFactory) { + return IntegrationFlows.from(...) + ... + .channel(Amqp.channel(connectionFactory) + .queueName("bar")) + ... + .get(); +} + +@Bean +public IntegrationFlow pubSubInFlow(ConnectionFactory connectionFactory) { + return IntegrationFlows.from(...) + ... + .channel(Amqp.publishSubscribeChannel(connectionFactory) + .queueName("baz")) + ... + .get(); +} +``` + +### [](#amqp-message-headers)AMQP 消息头 + +#### [](#overview-2)概述 + +Spring 集成 AMQP 适配器自动映射所有 AMQP 属性和标头。(这是从 4.3-以前,只有标准标题被映射的变化)。默认情况下,这些属性通过使用[`DefaultAmqpHeaderMapper`](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/amqp/support/defaultamqpheadermapper.html)来复制到 Spring Integration`MessageHeaders`。 + +你可以传入你自己的特定于 AMQP 的头映射器的实现,因为适配器具有支持这样做的属性。 + +AMQP[`MessageProperties`](https://DOCS. Spring.io/ Spring-amqp/api/org/springframework/amqp/core/messageproperties.html)中的任何用户定义的头都被复制到或从 AMQP 消息复制,除非`requestHeaderNames`或`replyHeaderNames`属性的`DefaultAmqpHeaderMapper`明确否定。默认情况下,对于出站映射器,不映射`x-*`标头。关于原因,请参见本节后面出现的[caution](#header-copy-caution)。 + +要覆盖默认值并恢复到 pre-4.3 行为,请在属性中使用`STANDARD_REQUEST_HEADERS`和`STANDARD_REPLY_HEADERS`。 + +| |在映射用户定义的标头时,值还可以包含要匹配的简单通配符模式(例如`thing*`或`*thing`)。
`*`匹配所有标头。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 4.1 开始,`AbstractHeaderMapper`(a`DefaultAmqpHeaderMapper`超类)允许将`NON_STANDARD_HEADERS`令牌配置为`requestHeaderNames`和`replyHeaderNames`属性(除了现有的`STANDARD_REQUEST_HEADERS`和`STANDARD_REPLY_HEADERS`),以映射所有用户定义的标题。 + +`org.springframework.amqp.support.AmqpHeaders`类标识了`DefaultAmqpHeaderMapper`所使用的默认标头: + +* `amqp_appId` + +* `amqp_clusterId` + +* `amqp_contentEncoding` + +* `amqp_contentLength` + +* `content-type`(参见[the`contentType`header](#amqp-content-type)) + +* `amqp_correlationId` + +* `amqp_delay` + +* `amqp_deliveryMode` + +* `amqp_deliveryTag` + +* `amqp_expiration` + +* `amqp_messageCount` + +* `amqp_messageId` + +* `amqp_receivedDelay` + +* `amqp_receivedDeliveryMode` + +* `amqp_receivedExchange` + +* `amqp_receivedRoutingKey` + +* `amqp_redelivered` + +* `amqp_replyTo` + +* `amqp_timestamp` + +* `amqp_type` + +* `amqp_userId` + +* `amqp_publishConfirm` + +* `amqp_publishConfirmNackCause` + +* `amqp_returnReplyCode` + +* `amqp_returnReplyText` + +* `amqp_returnExchange` + +* `amqp_returnRoutingKey` + +* `amqp_channel` + +* `amqp_consumerTag` + +* `amqp_consumerQueue` + +| |正如在本节前面提到的,使用`*`的标头映射模式是复制所有标头的一种常见方法,
但是,这可能会有一些意想不到的副作用,因为某些 RabbitMQ 专有属性/标头也会被复制。
例如,当你使用[federation](https://www.rabbitmq.com/federated-exchanges.html)时,接收到的消息可能有一个名为`x-received-from`的属性,其中包含发送消息的节点,
如果使用通配符`*`进行入站网关上的请求和回复头映射,则会复制此头,这可能会导致联合出现一些问题,
此回复消息可能会被联合回发送代理,后者可能会认为消息正在循环,因此会静默地删除它,
如果你希望使用通配符标头映射的便利,你可能需要在下游流中过滤掉一些头。
例如,为了避免将`x-received-from`头复制回答复,你可以在将答复发送到 AMQP 入站网关之前使用``。
或者,你可以显式地列出实际需要映射的那些属性,而不是使用通配符。
由于这些原因,对于入站消息,映射器(默认情况下)不会映射任何`x-*`标头。
它也不会将标头映射到`amqp_deliveryMode`标头,为了避免该报头从入站消息传播到出站消息。
相反,此报头被映射到`amqp_receivedDeliveryMode`,这在输出上没有映射。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 4.3 开始,头映射中的模式可以通过使用`!`在模式之前被否定。被否定的模式获得优先权,所以像`STANDARD_REQUEST_HEADERS,thing1,ba*,!thing2,!thing3,qux,!thing1`这样的列表不映射`thing1`(nor`thing2`nor`thing3`)。标准标题加上`bad`和`qux`被映射。例如,当 JSON 反序列化逻辑在下游的接收器以不同的方式完成时,否定技术对于不映射 JSON 类型报头的传入消息是有用的。为此,应该为入站通道适配器/网关的头映射器配置`!json_*`模式。 + +| |如果你有一个以`!`开头的用户定义标头,你确实希望对其进行映射,那么你需要将其转换为`\`,如下所示:`STANDARD_REQUEST_HEADERS,\!myBangHeader`。
现在映射了名为`!myBangHeader`的标头。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |从版本 5.1 开始,`DefaultAmqpHeaderMapper`将分别回到映射`MessageHeaders.ID`和`MessageHeaders.TIMESTAMP`到`MessageProperties.messageId`和`MessageProperties.timestamp`,如果出站消息上不存在相应的`amqp_messageId`或`amqp_timestamp`标头。
入站属性将像以前一样映射到`amqp_*`标头。
当消息消费者使用有状态重试时,填充`messageId`属性是有用的。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#amqp-content-type)标题`contentType` + +与其他头不同,`AmqpHeaders.CONTENT_TYPE`不带`amqp_`前缀;这允许在不同的技术之间透明地传递 ContentType 头。例如,发送到 RabbitMQ 队列的入站 HTTP 消息。 + +将`contentType`头映射到 Spring AMQP 的`MessageProperties.contentType`属性,然后将其映射到 RabbitMQ 的`content_type`属性。 + +在版本 5.1 之前,这个头也被映射为`MessageProperties.headers`映射中的一个条目;这是不正确的,而且,该值可能是错误的,因为底层的 Spring AMQP 消息转换器可能已经更改了内容类型。这样的更改将反映在第一类`content_type`属性中,但不会反映在 RabbitMQ 头文件映射中。入站映射忽略了 headers 映射值。`contentType`不再映射到 headers 映射中的条目。 + +### [](#amqp-strict-ordering)严格的消息排序 + +本节描述了入站和出站消息的消息排序。 + +####
入站 + +如果需要对入站消息进行严格排序,则必须将入站侦听器容器的`prefetchCount`属性配置为`1`。这是因为,如果一条消息失败并被重新传递,它将在已有的预取消息之后到达。自 Spring AMQP 版本 2.0 以来,`prefetchCount`默认为`250`,以提高性能。严格的订货要求是以性能下降为代价的。 + +#### [](#outbound)出站 + +考虑以下集成流程: + +``` +@Bean +public IntegrationFlow flow(RabbitTemplate template) { + return IntegrationFlows.from(Gateway.class) + .split(s -> s.delimiters(",")) + .transform(String::toUpperCase) + .handle(Amqp.outboundAdapter(template).routingKey("rk")) + .get(); +} +``` + +假设我们将消息`A`、`B`和`C`发送到网关。虽然消息`A`、`B`、`C`很可能是按顺序发送的,但不能保证。这是因为模板为每次发送操作从缓存中“借用”一个通道,并且不能保证每个消息都使用相同的通道。一种解决方案是在拆分器之前启动事务,但是事务在 RabbitMQ 中是昂贵的,并且可以将性能降低数百倍。 + +为了以更有效的方式解决这个问题,从版本 5.1 开始, Spring 集成提供了`BoundRabbitChannelAdvice`这是一个`HandleMessageAdvice`。见[处理消息建议](./handler-advice.html#handle-message-advice)。当在拆分器之前应用时,它确保所有下游操作都在相同的通道上执行,并且可以选择地等待,直到接收到所有已发送消息的发布者确认(如果连接工厂被配置为确认)。下面的示例展示了如何使用`BoundRabbitChannelAdvice`: + +``` +@Bean +public IntegrationFlow flow(RabbitTemplate template) { + return IntegrationFlows.from(Gateway.class) + .split(s -> s.delimiters(",") + .advice(new BoundRabbitChannelAdvice(template, Duration.ofSeconds(10)))) + .transform(String::toUpperCase) + .handle(Amqp.outboundAdapter(template).routingKey("rk")) + .get(); +} +``` + +注意,在通知和出站适配器中使用了相同的`RabbitTemplate`(它实现了`RabbitOperations`)。该建议在模板的`invoke`方法中运行下游流,以便所有操作都在同一个通道上运行。如果提供了可选的超时,当流完成时,通知调用`waitForConfirmsOrDie`方法,如果在指定的时间内没有收到确认,该方法将抛出一个异常。 + +| |在下游流中不能有线程切换(`QueueChannel`,`ExecutorChannel`,以及其他)。| +|---|--------------------------------------------------------------------------------------------------------| + +### [](#amqp-samples)AMQP 样本 + +要对 AMQP 适配器进行实验,请查看 Spring Integration Samples Git Repository 中可用的示例,网址为[https://github.com/SpringSource/spring-integration-samples](https://github.com/spring-projects/spring-integration-samples) + +目前,一个示例通过使用出站通道适配器和入站通道适配器来演示 Spring 集成 AMQP 适配器的基本功能。作为样例中的 AMQP 代理实现使用[RabbitMQ](https://www.rabbitmq.com/)。 + +| |为了运行示例,你需要一个正在运行的 RabbitMQ 实例。
只有基本默认值的本地安装就足够了。
有关 RabbitMQ 安装过程的详细信息,请参见[https://www.rabbitmq.com/install.html](https://www.rabbitmq.com/install.html)| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +示例应用程序启动后,在命令提示符中输入一些文本,然后将包含所输入文本的消息发送到 AMQP 队列。作为回报, Spring Integration 将检索该消息并将其打印到控制台。 + +下面的图像说明了 Spring 在此示例中使用的集成组件的基本集合。 + +![spring integration amqp sample graph](images/spring-integration-amqp-sample-graph.png) + +图 1。AMQP 样本的 Spring 积分图 \ No newline at end of file diff --git a/docs/spring-integration/configuration.md b/docs/spring-integration/configuration.md new file mode 100644 index 0000000..320856c --- /dev/null +++ b/docs/spring-integration/configuration.md @@ -0,0 +1,700 @@ +# 配置 + +## [](#configuration)配置 + +Spring 集成提供了许多配置选项。你选择哪一种选择取决于你的特殊需求,以及你更喜欢工作的级别。与 Spring 框架一般一样,你可以混合和匹配各种技术以适应手头的问题。例如,你可以为大多数配置选择基于 XSD 的名称空间,并将其与使用注释进行配置的几个对象结合在一起。两者尽可能地提供一致的命名。由 XSD 模式定义的 XML 元素与注释的名称匹配,并且这些 XML 元素的属性与注释属性的名称匹配。你也可以直接使用 API,但是我们希望大多数开发人员选择一个更高级别的选项,或者基于名称空间的配置和注释驱动的配置的组合。 + +### [](#configuration-namespace)名称空间支持 + +你可以使用 XML 元素配置 Spring 集成组件,这些 XML 元素直接映射到 Enterprise 集成的术语和概念。在许多情况下,元素名与[*Enterprise 整合模式 *](https://www.enterpriseintegrationpatterns.com/)书中的元素名匹配。 + +要在你的 Spring 配置文件中启用 Spring 集成的核心名称空间支持,请在你的顶级“bean”元素中添加以下名称空间引用和模式映射: + +``` + +``` + +(我们强调了 Spring 集成所特有的线条。) + +你可以在“xmlns:”之后选择任何名称。为了清晰起见,我们使用`int`(Integration 的缩写),但你可能更喜欢另一个缩写。另一方面,如果你使用 XML 编辑器或 IDE 支持,那么自动完成功能的可用性可能会说服你保留较长的名称以使其更清晰。或者,你也可以创建使用 Spring 集成模式作为主要名称空间的配置文件,如下例所示: + +``` + +``` + +(我们强调了 Spring 集成所特有的线条。) + +当使用此替代方案时, Spring 集成元素不需要前缀。另一方面,如果在相同的配置文件中定义一个泛型 Spring Bean,则 Bean 元素需要一个前缀(``)。由于模块化配置文件本身(基于责任层或体系结构层)通常是一个好主意,因此你可能会发现在以集成为中心的配置文件中使用后一种方法是合适的,因为在这些文件中很少需要泛型 bean。出于本文的目的,我们假设集成名称空间是主要的。 + +Spring 集成提供了许多其他名称空间。实际上,每个提供名称空间支持的适配器类型(JMS、文件等)都在一个单独的模式中定义其元素。为了使用这些元素,请添加带有`xmlns`条目和相应的`schemaLocation`映射的必要名称空间。例如,下面的根元素显示了其中的几个名称空间声明: + +``` + + + ... + +``` + +本参考手册在相应章节中提供了各种元素的具体示例。在这里,需要认识的主要问题是每个名称空间 URI 和模式位置的命名的一致性。 + +### [](#namespace-taskscheduler)配置任务调度程序 + +在 Spring 集成中,`ApplicationContext`扮演着消息总线的中心角色,你只需要考虑几个配置选项。首先,你可能想要控制中心`TaskScheduler`实例。你可以通过提供一个名为`taskScheduler`的 Bean 来实现此目的。这也被定义为一个常数,如下所示: + +``` +IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME +``` + +默认情况下, Spring 集成依赖于`ThreadPoolTaskScheduler`的实例,如 Spring 框架参考手册的[任务执行和调度](https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#scheduling)部分所述。默认的`TaskScheduler`会在有 10 个线程池的情况下自动启动,但请参见[全局属性](#global-properties)。如果你提供你自己的`TaskScheduler`实例,那么你可以将“自动启动”属性设置为`false`,或者提供你自己的池大小值。 + +当轮询使用者在其配置中提供显式的任务执行器引用时,处理程序方法的调用发生在执行器的线程池中,而不是主调度器池中。但是,当没有为端点的 Poller 提供任务执行器时,它会被主调度程序的一个线程调用。 + +| |不要在 Poller 线程上运行长时间运行的任务。
改为使用任务执行器,
如果有很多轮询端点,除非增加池大小,否则可能会导致线程短缺,
,轮询消费者的默认`receiveTimeout`为一秒。
由于这一次的 poller 线程阻塞,我们建议你在存在许多这样的端点时使用任务执行器,再次避免饥饿。
或者,你可以减少`receiveTimeout`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果一个端点的输入通道是一个基于队列的(也就是 pollable)通道,那么它就是一个轮询消费者。
事件驱动的消费者是那些拥有带有调度器而不是队列的输入通道的消费者(换句话说,它们是可下标的)。
这样的端点没有 poller 配置,因为它们的处理程序是直接调用的。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在 JEE 容器中运行时,你可能需要使用 Spring 的`TimerManagerTaskScheduler`,如所描述的[here](https://docs.spring.io/spring/docs/current/spring-framework-reference/integration.html#scheduling-task-scheduler-implementations),而不是默认的`taskScheduler`。
要这样做,请为你的环境定义一个具有适当 JNDI 名称的 Bean,如下例所示:

```




```| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |当在应用程序上下文中配置自定义`TaskScheduler`(如上面提到的`DefaultManagedTaskScheduler`)时,建议为它提供一个`MessagePublishingErrorHandler`(`integrationMessagePublishingErrorHandler` Bean),以便能够处理异常,因为`ErrorMessage`s 被发送到错误通道,正如框架提供的默认`TaskScheduler` Bean 所做的那样。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有关更多信息,请参见[错误处理](./error-handling.html#error-handling)。 + +### [](#global-properties)全局属性 + +可以通过在 Classpath 上提供一个属性文件来覆盖某些全局框架属性。 + +默认属性可以在`org.springframework.integration.context.IntegrationProperties`类中找到。下面的列表显示了默认值: + +``` +spring.integration.channels.autoCreate=true (1) +spring.integration.channels.maxUnicastSubscribers=0x7fffffff (2) +spring.integration.channels.maxBroadcastSubscribers=0x7fffffff (3) +spring.integration.taskScheduler.poolSize=10 (4) +spring.integration.messagingTemplate.throwExceptionOnLateReply=false (5) +spring.integration.readOnly.headers= (6) +spring.integration.endpoints.noAutoStartup= (7) +spring.integration.channels.error.requireSubscribers=true (8) +spring.integration.channels.error.ignoreFailures=true (9) +``` + +|**1**|如果为真,则在应用程序上下文中未显式找到`input-channel`实例时,将自动声明为`DirectChannel`实例。| +|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|**2**|设置默认的订阅人数,例如,一个`DirectChannel`。
它可以用来避免无意中订阅多个端点到同一个通道。
你可以通过设置`max-subscribers`属性在单个通道上覆盖它。| +|**3**|此属性提供了默认的订阅服务器数量,例如,`PublishSubscribeChannel`。
它可以用来避免无意中订阅超过预期数量的相同通道的端点。
你可以通过设置`max-subscribers`属性在单个通道上覆盖它。| +|**4**|默认情况下可用的线程数`taskScheduler` Bean。
参见[配置任务调度程序](#namespace-taskscheduler)。| +|**5**|当`true`时,到达网关回复通道的消息在网关不期望得到回复时(因为发送线程已超时或已收到回复)会引发异常。| +|**6**|用逗号分隔的消息头名称列表在头部复制操作期间,不应将其填充到`Message`实例中。
该列表由`DefaultMessageBuilderFactory` Bean 使用并传播到`IntegrationMessageHeaderAccessor`实例(参见[`MessageHeaderAccessor`API](./message.html#message-header-accessor)),用于通过`MessageBuilder`生成消息(参见[the`MessageBuilder`Helper 类](./message-builder r=“108”))。只有`MessageHeaders.ID`和`MessageHeaders.TIMESTAMP`在消息生成过程中不被复制。
自版本 4.3.2 起。| +|**7**|用逗号分隔的`AbstractEndpoint` Bean 名称模式(`xxx*`,`**xxx**`**,`*xxx`** 或`xxx*yyy`)的列表,在应用程序启动期间不应自动启动。
你可以稍后通过`Control Bus`通过其 Bean 名称手动启动这些端点,(参见[控制总线](./control-bus.html#control-bus)),它们在`SmartLifecycleRoleController`中的作用(见[端点角色](./endpoint.html#endpoint-roles)),或者通过`Lifecycle` Bean 注入。
你可以通过指定`auto-startup`XML 注释或`autoStartup`注释属性,或者通过在 Bean 定义中调用`AbstractEndpoint.setAutoStartup()`显式覆盖此全局属性的效果。
自 4.3.12 版本开始。| +|**8**|表示默认全局`errorChannel`必须配置`requireSubscribers`选项的布尔标志。
自版本 5.4.3 起。
有关更多信息,请参见[错误处理](./error-handling.html#error-handling)。| +|**9**|一个布尔标志,指示默认的全局`errorChannel`必须忽略调度错误并将消息传递给下一个处理程序。
自版本 5.5 起。| + +可以通过向 Classpath 中添加`/META-INF/spring.integration.properties`文件或为`org.springframework.integration.context.IntegrationProperties`实例添加`IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME` Bean 来重写这些属性。你不需要提供所有属性——只需要提供你想要覆盖的那些属性。 + +从版本 5.1 开始,当`org.springframework.integration`类别的`DEBUG`逻辑级别打开时,所有合并的全局属性都会在应用程序上下文启动后打印在日志中。输出如下所示: + +``` +Spring Integration global properties: + +spring.integration.endpoints.noAutoStartup=fooService* +spring.integration.taskScheduler.poolSize=20 +spring.integration.channels.maxUnicastSubscribers=0x7fffffff +spring.integration.channels.autoCreate=true +spring.integration.channels.maxBroadcastSubscribers=0x7fffffff +spring.integration.readOnly.headers= +spring.integration.messagingTemplate.throwExceptionOnLateReply=true +``` + +### [](#annotations)注释支持 + +除了对配置消息端点的 XML 命名空间的支持外,还可以使用注释。首先, Spring 集成提供了类级`@MessageEndpoint`作为原型注释,这意味着它本身用 Spring 的`@Component`注释,因此通过 Spring 的组件扫描自动识别为 Bean 定义。 + +更重要的是各种方法级别的注释。它们表示带注释的方法能够处理消息。下面的示例演示了类级和方法级的注释: + +``` +@MessageEndpoint +public class FooService { + + @ServiceActivator + public void processMessage(Message message) { + ... + } +} +``` + +方法“处理”消息的确切含义取决于特定的注释。 Spring 集成中提供的注释包括: + +* `@Aggregator`(见[Aggregator](./aggregator.html#aggregator)) + +* `@Filter`(见[Filter](./filter.html#filter)) + +* `@Router`(见[Routers](./router.html#router)) + +* `@ServiceActivator`(见[服务激活器](./service-activator.html#service-activator)) + +* `@Splitter`(见[Splitter](./splitter.html#splitter)) + +* `@Transformer`(见[变压器](./transformer.html#transformer)) + +* `@InboundChannelAdapter`(见[通道适配器](./channel-adapter.html#channel-adapter)) + +* `@BridgeFrom`(见[用 Java 配置配置桥](./bridge.html#bridge-annot)) + +* `@BridgeTo`(见[用 Java 配置配置桥](./bridge.html#bridge-annot)) + +* `@MessagingGateway`(见[消息传递网关](./gateway.html#gateway)) + +* `@IntegrationComponentScan`(参见[configuration and`@EnableIntegration`](./overview.html#configuration-enable-integration)) + +| |如果将 XML 配置与注释结合使用,则不需要`@MessageEndpoint`注释。,如果要从``元素的`ref`属性中配置 POJO 引用,则只能提供方法级别的注释。,在这种情况下,
,即使在``元素上不存在方法级属性时,该注释也可以防止歧义。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +在大多数情况下,带注释的处理程序方法不应该要求`Message`类型作为其参数。相反,方法参数类型可以匹配消息的有效负载类型,如下例所示: + +``` +public class ThingService { + + @ServiceActivator + public void bar(Thing thing) { + ... + } + +} +``` + +当方法参数应该从`MessageHeaders`中的值映射时,另一个选项是使用参数级别`@Header`注释。通常,使用 Spring 集成注释注释的方法可以接受`Message`本身、消息有效负载或头值(带有`@Header`)作为参数。实际上,该方法可以接受一种组合,如下例所示: + +``` +public class ThingService { + + @ServiceActivator + public void otherThing(String payload, @Header("x") int valueX, @Header("y") int valueY) { + ... + } + +} +``` + +你还可以使用`@Headers`注释来提供所有消息头作为`Map`,如下例所示: + +``` +public class ThingService { + + @ServiceActivator + public void otherThing(String payload, @Headers Map headerMap) { + ... + } + +} +``` + +| |注释的值也可以是一个 SPEL 表达式(例如,`someHeader.toUpperCase()`),当你希望在注入标头值之前对标头值进行操作时,该表达式非常有用,
它还提供了一个可选的`required`属性,它指定属性值是否必须在标题中可用。
`required`属性的默认值是`true`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +对于其中的几个注释,当消息处理方法返回一个非空值时,端点将尝试发送一个答复。这在两种配置选项(名称空间和注释)中都是一致的,因为使用了这样的端点输出通道(如果可用的话),并且使用`REPLY_CHANNEL`消息头值作为后备。 + +| |端点上的输出通道和应答通道消息头的组合实现了一种流水线方法,在这种方法中,多个组件具有一个输出通道,而最终组件允许将应答消息转发到应答通道(如原始请求消息中指定的那样)。,换句话说,
,最后的组件依赖于原始发送者提供的信息,并可以动态地支持任意数量的客户端作为结果。
这是[回邮地址](https://www.enterpriseintegrationpatterns.com/ReturnAddress.html)模式的一个示例。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +除了这里显示的示例之外,这些注释还支持`inputChannel`和`outputChannel`属性,如下例所示: + +``` +@Service +public class ThingService { + + @ServiceActivator(inputChannel="input", outputChannel="output") + public void otherThing(String payload, @Headers Map headerMap) { + ... + } + +} +``` + +这些注释的处理创建了与相应的 XML 组件相同的 bean—`AbstractEndpoint`实例和`MessageHandler`实例(或入站通道适配器的`MessageSource`实例)。参见[关于`@Bean`方法的注释]。 Bean 名称是从以下模式生成的:`[componentName].[methodName].[decapitalizedAnnotationClassShortName]`。在前面的示例中, Bean 名称为`AbstractEndpoint`的`thingService.otherThing.serviceActivator`,而与`.handler`(`.source`) Bean 的后缀为`MessageHandler`(`MessageSource`)。可以使用`@EndpointId`注释以及这些消息传递注释来定制这样的名称。`MessageHandler`实例(`MessageSource`实例)也有资格被[消息历史](./message-history.html#message-history)跟踪。 + +从版本 4.0 开始,所有消息传递注释都提供`SmartLifecycle`选项(`autoStartup`和`phase`),以允许对应用程序上下文初始化进行端点生命周期控制。它们分别默认为`true`和`0`。要更改端点的状态(例如` start()`或`stop()`),可以通过使用`BeanFactory`(或自动布线)获得对端点 Bean 的引用并调用方法。或者,你可以向`Control Bus`发送命令消息(参见[控制总线](./control-bus.html#control-bus))。为了这些目的,你应该使用上一段前面提到的`beanName`。 + +| |在解析所提及的注释(当没有配置特定的通道 Bean 时)之后自动创建的通道和相应的消费者端点,在上下文初始化的末尾附近被声明为 bean。
这些 bean**CAN**在其他服务中被自动连线,但它们必须用`@Lazy`注释标记,因为这些定义通常在正常的自动布线处理过程中还不可用。

```
@Autowired
@Lazy
@Qualifier("someChannel")
MessageChannel someChannel;
...

@Bean
Thing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannelAdapter someInboundAdapter) {
...
}
```| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#configuration-using-poller-annotation)使用`@Poller`注释 + +Spring Integration4.0 之前,消息传递注释要求`inputChannel`是对`SubscribableChannel`的引用。对于`PollableChannel`实例,需要一个``元素来配置``并使复合端点为`PollingConsumer`。版本 4.0 引入了`@Poller`注释,以允许直接在消息传递注释上配置`poller`属性,如下例所示: + +``` +public class AnnotationService { + + @Transformer(inputChannel = "input", outputChannel = "output", + poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}")) + public String handle(String payload) { + ... + } +} +``` + +`@Poller`注释仅提供简单的`PollerMetadata`选项。你可以使用属性占位符配置`@Poller`注释的属性(`maxMessagesPerPoll`、`fixedDelay`、`fixedRate`和`cron`)。此外,从版本 5.1 开始,还提供了`receiveTimeout`s 的`PollingConsumer`选项。如果需要提供更多的轮询选项(例如,`transaction`,`advice-chain`,`error-handler`,以及其他),则应将`PollerMetadata`配置为泛型 Bean,并将其 Bean 名称用作`@Poller`的`value`属性。在这种情况下,不允许其他属性(它们必须在`PollerMetadata` Bean 上指定)。注意,如果`inputChannel`是`PollableChannel`,并且没有配置`@Poller`,则使用默认的`PollerMetadata`(如果它存在于应用程序上下文中)。要通过使用`@Configuration`注释来声明默认的 Poller,请使用类似于以下示例的代码: + +``` +@Bean(name = PollerMetadata.DEFAULT_POLLER) +public PollerMetadata defaultPoller() { + PollerMetadata pollerMetadata = new PollerMetadata(); + pollerMetadata.setTrigger(new PeriodicTrigger(10)); + return pollerMetadata; +} +``` + +下面的示例展示了如何使用默认的 Poller: + +``` +public class AnnotationService { + + @Transformer(inputChannel = "aPollableChannel", outputChannel = "output") + public String handle(String payload) { + ... + } +} +``` + +下面的示例展示了如何使用命名的 Poller: + +``` +@Bean +public PollerMetadata myPoller() { + PollerMetadata pollerMetadata = new PollerMetadata(); + pollerMetadata.setTrigger(new PeriodicTrigger(1000)); + return pollerMetadata; +} +``` + +下面的示例显示了一个使用默认 Poller 的端点: + +``` +public class AnnotationService { + + @Transformer(inputChannel = "aPollableChannel", outputChannel = "output" + poller = @Poller("myPoller")) + public String handle(String payload) { + ... + } +} +``` + +从版本 4.3.3 开始,`@Poller`注释具有`errorChannel`属性,以便更轻松地配置底层`MessagePublishingErrorHandler`。此属性在``XML 组件中扮演与`error-channel`相同的角色。有关更多信息,请参见[端点命名空间支持](./endpoint.html#endpoint-namespace)。 + +消息传递注释上的`poller()`属性与`reactive()`属性是互斥的。有关更多信息,请参见下一节。 + +#### [](#configuration-using-reactive-annotation)使用`@Reactive`注释 + +`ReactiveStreamsConsumer`自版本 5.0 以来一直存在,但它仅在端点的输入通道是`FluxMessageChannel`(或任何`org.reactivestreams.Publisher`实现)时才应用。从版本 5.3 开始,当目标消息处理程序是独立于输入通道类型的`ReactiveMessageHandler`时,框架也会创建它的实例。对于从版本 5.5 开始的所有消息传递注释,都引入了`@Reactive`子注释(类似于上面提到的`@Poller`)。它接受一个可选的`Function>, ? extends Publisher>>` Bean 引用,并且独立于输入通道类型和消息处理程序,将目标端点转换为`ReactiveStreamsConsumer`实例。该函数使用来自`Flux.transform()`操作符的一些自定义(`publishOn()`,`doOnNext()`,`log()`,`retry()`等)在来自输入通道的反应流上应用。 + +下面的示例演示了如何将发布线程从独立于最终订阅者和生产者的输入通道更改为`DirectChannel`: + +``` +@Bean +public Function, Flux> publishOnCustomizer() { + return flux -> flux.publishOn(Schedulers.parallel()); +} + +@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer")) +public void handleReactive(String payload) { + ... +} +``` + +消息传递注释上的`reactive()`属性与`poller()`属性是互斥的。有关更多信息,请参见[使用`@Poller`注释](#configuration-using-poller-annotation)和[反应流支持](./reactive-streams.html#reactive-streams)。 + +#### [](#using-the-inboundchanneladapter-annotation)使用`@InboundChannelAdapter`注释 + +版本 4.0 引入了`@InboundChannelAdapter`方法级注释。它为带注释的方法基于`MethodInvokingMessageSource`生成`SourcePollingChannelAdapter`集成组件。该注释类似于``XML 组件,并且具有相同的限制:该方法不能具有参数,并且返回类型不能是`void`。它有两个属性:`value`(要求的`MessageChannel` Bean name)和`poller`(可选的`@Poller`注释,如[前面描述的](#configuration-using-poller-annotation))。如果需要提供一些`MessageHeaders`,使用`Message`返回类型并使用`MessageBuilder`来构建`Message`。使用`MessageBuilder`可以配置`MessageHeaders`。下面的示例展示了如何使用`@InboundChannelAdapter`注释: + +``` +@InboundChannelAdapter("counterChannel") +public Integer count() { + return this.counter.incrementAndGet(); +} + +@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000")) +public String foo() { + return "foo"; +} +``` + +版本 4.3 引入了`channel`的`value`注释属性的别名,以提供更好的源代码可读性。同样,目标`MessageChannel` Bean 在`SourcePollingChannelAdapter`中通过第一个`outputChannelName`调用上提供的名称(由`outputChannelName`选项设置)进行解析,而不是在初始化阶段。它允许“后期绑定”逻辑:从消费者的角度来看,目标`MessageChannel` Bean 的创建和注册要比`@InboundChannelAdapter`解析阶段晚一点。 + +第一个示例要求在应用程序上下文的其他地方声明了默认的 Poller。 + +使用`@MessagingGateway`注释 + +参见[`@MessagingGateway`注释](./gateway.html#messaging-gateway-annotation)。 + +#### [](#using-the-integrationcomponentscan-annotation)使用`@IntegrationComponentScan`注释 + +标准的 Spring 框架`@ComponentScan`注释不扫描接口中的原型`@Component`注释。为了克服这个限制并允许`@MessagingGateway`的配置(参见[`@MessagingGateway`注释](./gateway.html#messaging-gateway-annotation)),我们引入了`@IntegrationComponentScan`机制。此注释必须与`@Configuration`注释一起放置,并进行自定义以定义其扫描选项,例如`basePackages`和`basePackageClasses`。在这种情况下,所有发现的带有`@MessagingGateway`注释的接口都被解析并注册为`GatewayProxyFactoryBean`实例。所有其他基于类的组件都由标准`@ComponentScan`解析。 + +### [](#meta-annotations)消息传递元注释 + +从版本 4.0 开始,所有消息传递注释都可以配置为元注释,并且所有用户定义的消息传递注释都可以定义相同的属性来覆盖其默认值。此外,元注释可以按层次进行配置,如下例所示: + +``` +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@ServiceActivator(inputChannel = "annInput", outputChannel = "annOutput") +public @interface MyServiceActivator { + + String[] adviceChain = { "annAdvice" }; +} + +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@MyServiceActivator +public @interface MyServiceActivator1 { + + String inputChannel(); + + String outputChannel(); +} +... + +@MyServiceActivator1(inputChannel = "inputChannel", outputChannel = "outputChannel") +public Object service(Object payload) { + ... +} +``` + +分层配置元注释使用户可以为各种属性设置默认值,并使 Framework Java 依赖与用户注释隔离,从而避免在用户类中使用它们。如果框架找到了一个具有框架元注释的用户注释的方法,则将其视为直接使用框架注释对该方法进行了注释。 + +#### [](#annotations_on_beans)方法上的注释 + +从版本 4.0 开始,你可以在`@Configuration`类中的`@Bean`方法定义上配置消息注释,以基于 bean 而不是方法生成消息端点。当`@Bean`定义是“开箱即用”`MessageHandler`实例(`AggregatingMessageHandler`,`DefaultMessageSplitter`,以及其他),`Transformer`实例(`JsonToObjectTransformer`,`ClaimCheckOutTransformer`,以及其他),以及`MessageSource`实例(`FileReadingMessageSource`,`RedisStoreMessageSource`,以及其他)时,它是有用的。下面的示例展示了如何使用带有`@Bean`注释的消息传递注释: + +``` +@Configuration +@EnableIntegration +public class MyFlowConfiguration { + + @Bean + @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) + public MessageSource consoleSource() { + return CharacterStreamReadingMessageSource.stdin(); + } + + @Bean + @Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel") + public ObjectToMapTransformer toMapTransformer() { + return new ObjectToMapTransformer(); + } + + @Bean + @ServiceActivator(inputChannel = "httpChannel") + public MessageHandler httpHandler() { + HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://foo/service"); + handler.setExpectedResponseType(String.class); + handler.setOutputChannelName("outputChannel"); + return handler; + } + + @Bean + @ServiceActivator(inputChannel = "outputChannel") + public LoggingHandler loggingHandler() { + return new LoggingHandler("info"); + } + +} +``` + +版本 5.0 引入了对`@Bean`注释的`@InboundChannelAdapter`的支持,该注释返回`java.util.function.Supplier`,可以生成 POJO 或`Message`。下面的示例展示了如何使用该组合: + +``` +@Configuration +@EnableIntegration +public class MyFlowConfiguration { + + @Bean + @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) + public Supplier pojoSupplier() { + return () -> "foo"; + } + + @Bean + @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) + public Supplier> messageSupplier() { + return () -> new GenericMessage<>("foo"); + } +} +``` + +元注释规则也适用于`@Bean`方法(`@MyServiceActivator`注释[前面描述的](#meta-annotations)可以应用于`@Bean`定义)。 + +| |在使用者`@Bean`定义上使用这些注释时,如果 Bean 定义返回适当的`MessageHandler`(取决于注释类型),则必须设置属性(例如`outputChannel`,`requiresReply`,`order`,以及其他属性),在`MessageHandler``@Bean`定义本身上。
只使用以下注释属性:`adviceChain`、`autoStartup`、`inputChannel`、`phase`和`poller`。
所有其他属性都是处理程序的。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Bean 名称是通过以下算法生成的:| +|---|----------------------------------------------------------| + +* `MessageHandler`(`MessageSource`)`@Bean`从`@Bean`上的方法名或`name`属性获得自己的标准名称。这就好像在`@Bean`方法上没有消息注释一样。 + +* 生成`AbstractEndpoint` Bean 名称的模式如下:`[configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName]`。例如,`SourcePollingChannelAdapter`的`consoleSource()`定义[显示在前面](#annotations_on_beans)的端点得到一个 Bean 名`myFlowConfiguration.consoleSource.inboundChannelAdapter`。另见[Endpoint Bean Names](./overview.html#endpoint-bean-names)。 + +| |当在`@Bean`定义上使用这些注释时,`inputChannel`必须引用已声明的 Bean。在这种情况下,
通道不会自动声明。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |使用 Java 配置,你可以在`@Bean`方法级别上使用任何`@Conditional`(例如,`@Profile`)的定义来跳过 Bean 注册的某些条件原因。
下面的示例展示了如何这样做:

```
@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("thing")
public MessageHandler skipped() {
return System.out::println;
}
```连同现有的容器逻辑一起,消息传递端点 Bean(基于`@ServiceActivator`注释)也未注册。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#creating-a-bridge-with-annotations)创建带有注释的桥 + +从版本 4.0 开始,Java Configuration 提供`@BridgeFrom`和`@BridgeTo``@Bean`方法注释,以便在`MessageChannel`类中标记`MessageChannel`bean。这些确实是为了完整性而存在的,提供了一种方便的机制来声明`BridgeHandler`及其消息端点配置: + +``` +@Bean +public PollableChannel bridgeFromInput() { + return new QueueChannel(); +} + +@Bean +@BridgeFrom(value = "bridgeFromInput", poller = @Poller(fixedDelay = "1000")) +public MessageChannel bridgeFromOutput() { + return new DirectChannel(); +} +@Bean +public QueueChannel bridgeToOutput() { + return new QueueChannel(); +} + +@Bean +@BridgeTo("bridgeToOutput") +public MessageChannel bridgeToInput() { + return new DirectChannel(); +} +``` + +你也可以将这些注释用作元注释。 + +#### [](#advising-annotated-endpoints)通知带注释的端点 + +见[使用注释为端点提供建议](./handler-advice.html#advising-with-annotations)。 + +### [](#message-mapping-rules)消息映射规则和约定 + +Spring 集成实现了一种灵活的功能,通过依赖一些默认规则和定义某些约定,在不提供额外配置的情况下将消息映射到方法及其参数。以下各节中的示例阐明了这些规则。 + +#### [](#sample-scenarios)示例场景 + +下面的示例显示了一个未注释的参数(对象或原语),它不是具有非空返回类型的`Map`或`Properties`对象: + +``` +public String doSomething(Object o); +``` + +输入参数是消息有效负载。如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。返回值被合并为返回消息的有效负载。 + +下面的示例显示了一个未注释的参数(对象或原语),该参数不是`Map`或`Properties`,返回类型为`Message`: + +``` +public Message doSomething(Object o); +``` + +输入参数是消息有效负载。如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。返回值是一个新构造的消息,它被发送到下一个目的地。 + +下面的示例显示了一个参数,它是具有任意对象或原始返回类型的消息(或其子类之一): + +``` +public int doSomething(Message msg); +``` + +输入参数本身是`Message`。返回值成为发送到下一个目的地的`Message`的有效负载。 + +下面的示例显示了一个参数`Message`(或其子类之一),并以`Message`(或其子类之一)作为返回类型: + +``` +public Message doSomething(Message msg); +``` + +输入参数本身是`Message`。返回值是一个新构造的`Message`,它被发送到下一个目标。 + +下面的示例显示了类型为`Map`或`Properties`的单个参数,其返回类型为`Message`: + +``` +public Message doSomething(Map m); +``` + +这个有点有趣。虽然一开始它看起来像是直接映射到消息头的简单映射,但是总是优先考虑`Message`有效负载。这意味着,如果`Message`有效载荷类型为`Map`,则此输入参数表示`Message`有效载荷。但是,如果`Message`有效负载不是`Map`类型,则转换服务不尝试转换有效负载,并且将输入参数映射到消息头。 + +下面的示例显示了两个参数,其中一个是不是`Map`或`Properties`对象的任意类型(对象或原语),另一个是`Map`或`Properties`类型(无论返回如何): + +``` +public Message doSomething(Map h, t); +``` + +这个组合包含两个输入参数,其中一个参数的类型为`Map`。将非`Map`参数(无论顺序如何)映射到`Message`有效负载,并将`Map`或`Properties`(无论顺序如何)映射到消息头,从而为你提供了一种与`Message`结构进行交互的良好的 POJO 方式。 + +下面的示例不显示参数(无论返回的是什么): + +``` +public String doSomething(); +``` + +此消息处理程序方法是基于发送到此处理程序所连接的输入通道的消息而调用的。但是没有映射`Message`数据,因此使`Message`充当事件或触发器来调用处理程序。根据规则[前面描述的](#message-mapping-rules)映射输出。 + +下面的示例不显示参数和 void 返回: + +``` +public void soSomething(); +``` + +这个示例与前面的示例相同,但不产生输出。 + +#### [](#annotation-based-mapping)基于注释的映射 + +基于注释的映射是将消息映射到方法的最安全、最不模糊的方法。下面的示例展示了如何显式地将方法映射到标头: + +``` +public String doSomething(@Payload String s, @Header("someheader") String b) +``` + +正如你稍后所看到的,如果没有注释,这个签名将导致一个模棱两可的条件。然而,通过显式地将第一个参数映射到`Message`有效负载,并将第二个参数映射到`someheader`消息头的值,我们避免了任何歧义。 + +下面的示例与前面的示例几乎相同: + +``` +public String doSomething(@Payload String s, @RequestParam("something") String b) +``` + +`@RequestMapping`或任何其他非 Spring 积分映射注释是不相关的,因此被忽略,使第二个参数未映射。尽管第二个参数可以很容易地映射到一个有效负载,但只能有一个有效负载。因此,注释避免了该方法的模棱两可。 + +下面的示例展示了另一种类似的方法,如果不是通过注释来澄清意图,这种方法将是模棱两可的: + +``` +public String foo(String s, @Header("foo") String b) +``` + +唯一的区别是,第一个参数隐式地映射到消息有效负载。 + +下面的示例展示了另一个签名,该签名在没有注释的情况下肯定会被视为模棱两可,因为它有两个以上的参数: + +``` +public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar) +``` + +这个示例将特别有问题,因为它的两个参数是`Map`实例。然而,使用基于注释的映射,可以很容易地避免歧义。在这个示例中,第一个参数映射到所有消息头,而第二个和第三个参数映射到名为“Something”和“Somethothing”的消息头的值。有效载荷没有被映射到任何参数。 + +#### [](#complex-scenarios)复杂情景 + +下面的示例使用了多个参数: + +在确定适当的映射时,多个参数可能会产生很多歧义。一般的建议是用`@Payload`、`@Header`和`@Headers`注释方法参数。本节中的示例显示了导致引发异常的模棱两可的条件。 + +``` +public String doSomething(String s, int i) +``` + +这两个参数的权重相等。因此,没有办法确定哪一个是有效载荷。 + +下面的示例显示了一个类似的问题,只包含三个参数: + +``` +public String foo(String s, Map m, String b) +``` + +尽管映射可以很容易地映射到消息头,但无法确定如何处理这两个字符串参数。 + +下面的示例展示了另一个模棱两可的方法: + +``` +public String foo(Map m, Map f) +``` + +尽管有人可能认为,一个`Map`可以映射到消息有效负载,另一个可以映射到消息头,但我们不能依赖顺序。 + +| |具有多个不是(map,\)的方法参数和未注释的参数的任何方法签名都会导致模棱两可的情况并触发异常。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下一组示例都展示了导致歧义的多种方法。 + +具有多个方法的消息处理程序是基于前面(在示例中)描述的相同规则进行映射的。然而,有些情况看起来仍然令人困惑。 + +下面的示例展示了具有合法(可映射和明确的)签名的多个方法: + +``` +public class Something { + public String doSomething(String str, Map m); + + public String doSomething(Map m); +} +``` + +(无论这些方法的名称是相同的,还是不同的,都没有区别)。`Message`可以映射到任意一个方法。当消息有效负载可以映射到`str`并且消息头可以映射到`m`时,将调用第一个方法。第二种方法也可以通过仅将消息头映射到`m`而成为候选方法。更糟糕的是,这两种方法名称相同。首先,由于以下配置,这可能看起来很模糊: + +``` + + + +``` + +它之所以有效,是因为映射首先基于有效负载,然后才是其他所有的东西。换句话说,第一个参数可以映射到有效负载的方法优先于所有其他方法。 + +现在考虑另一个例子,它产生了一个真正不明确的条件: + +``` +public class Something { + public String doSomething(String str, Map m); + + public String doSomething(String str); +} +``` + +这两个方法都有可以映射到消息有效负载的签名。他们也有相同的名字。这样的处理程序方法将触发异常。但是,如果方法名称不同,则可能会使用`method`属性来影响映射(如下一个示例所示)。下面的示例显示了具有两个不同方法名称的相同示例: + +``` +public class Something { + public String doSomething(String str, Map m); + + public String doSomethingElse(String str); +} +``` + +下面的示例展示了如何使用`method`属性来指定映射: + +``` + + + +``` + +因为配置显式地映射了`doSomethingElse`方法,所以我们消除了歧义。 \ No newline at end of file diff --git a/docs/spring-integration/core.md b/docs/spring-integration/core.md new file mode 100644 index 0000000..ec8cdba --- /dev/null +++ b/docs/spring-integration/core.md @@ -0,0 +1,1275 @@ +# 核心消息传递 + +## [](#messaging-channels-section)消息传递通道 + +### [](#channel)消息通道 + +虽然`Message`在封装数据方面发挥着关键作用,但将消息生产者与消息消费者分离开来的是`MessageChannel`。 + +#### [](#channel-interfaces)MessageChannel 接口 + +Spring 集成的顶级`MessageChannel`接口定义如下: + +``` +public interface MessageChannel { + + boolean send(Message message); + + boolean send(Message message, long timeout); +} +``` + +发送消息时,如果消息发送成功,则返回值为`true`。如果发送调用超时或被中断,它将返回`false`。 + +##### [](#channel-interfaces-pollablechannel)`PollableChannel` + +由于消息通道可能会或可能不会缓冲消息(如[Spring Integration Overview](./overview.html#overview)中所讨论的),因此两个子接口定义了缓冲和非缓冲通道行为。下面的清单显示了`PollableChannel`接口的定义: + +``` +public interface PollableChannel extends MessageChannel { + + Message receive(); + + Message receive(long timeout); + +} +``` + +与发送方法一样,当接收到消息时,在超时或中断的情况下,返回值为 null。 + +##### [](#channel-interfaces-subscribablechannel)`SubscribableChannel` + +`SubscribableChannel`基本接口是通过将消息直接发送到其订阅的`MessageHandler`实例的通道来实现的。因此,它们不提供用于轮询的接收方法。相反,他们定义了管理这些订阅者的方法。下面的清单显示了`SubscribableChannel`接口的定义: + +``` +public interface SubscribableChannel extends MessageChannel { + + boolean subscribe(MessageHandler handler); + + boolean unsubscribe(MessageHandler handler); + +} +``` + +#### [](#channel-implementations)消息通道实现 + +Spring 集成提供了几种不同的消息通道实现方式。下面几节简要介绍每一种方法。 + +##### [](#channel-implementations-publishsubscribechannel)`PublishSubscribeChannel` + +`PublishSubscribeChannel`实现将发送给它的任何`Message`广播给其所有订阅的处理程序。这通常用于发送事件消息,其主要作用是通知(与文档消息相反,文档消息通常由单个处理程序处理)。请注意,`PublishSubscribeChannel`仅用于发送。由于它在调用`send(Message)`方法时直接向订阅者广播,因此消费者无法轮询消息(它没有实现`PollableChannel`,因此没有`receive()`方法)。相反,任何订阅服务器本身必须是`MessageHandler`,并且依次调用订阅服务器的`handleMessage(Message)`方法。 + +在版本 3.0 之前,在没有订阅服务器的`PublishSubscribeChannel`上调用`send`方法返回`false`。当与`MessagingTemplate`一起使用时,将抛出`MessageDeliveryException`。从版本 3.0 开始,行为发生了变化,如果至少存在最小的订阅服务器(并成功处理消息),则`send`总是被认为是成功的。可以通过设置`minSubscribers`属性来修改此行为,该属性的默认值为`0`。 + +| |如果使用`TaskExecutor`,则仅使用存在正确数量的订阅服务器来进行此确定,因为消息的实际处理是异步执行的。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#channel-implementations-queuechannel)`QueueChannel` + +`QueueChannel`实现封装了一个队列。与`PublishSubscribeChannel`不同,`QueueChannel`具有点对点语义。换句话说,即使该通道有多个消费者,其中只有一个应该接收到发送到该通道的任何`Message`。它提供了一个默认的无参数构造函数(提供了一个基本上是无界的`Integer.MAX_VALUE`的容量),以及一个接受队列容量的构造函数,如下面的清单所示: + +``` +public QueueChannel(int capacity) +``` + +未达到容量限制的通道将消息存储在其内部队列中,并且`send(Message)`方法立即返回,即使没有接收者准备好处理该消息。如果队列已达到容量,发送方将阻塞该队列,直到该队列中有可用的空间为止。或者,如果使用具有附加超时参数的 send 方法,则队列将阻塞,直到可用的房间或超时周期(以先发生的为准)过去为止。类似地,如果队列上有消息可用,则`receive()`调用将立即返回,但是,如果队列是空的,则接收调用可能会阻塞,直到消息可用或超时(如果提供了超时)为止。在这两种情况下,都可以通过传递 0 的超时值来强制立即返回,而不管队列的状态如何。但是,请注意,这将无限期地调用`send()`和`receive()`参数块的版本。 + +##### [](#channel-implementations-prioritychannel)`PriorityChannel` + +虽然`QueueChannel`强制执行先进先出排序,但`PriorityChannel`是一种替代实现,它允许基于优先级在通道内对消息进行排序。默认情况下,优先级由每个消息中的`priority`头决定。但是,对于自定义优先级确定逻辑,可以向`Comparator>`构造函数提供类型`PriorityChannel`的比较器。 + +##### [](#channel-implementations-rendezvouschannel)`RendezvousChannel` + +`RendezvousChannel`启用了一个“直接切换”场景,其中,发送方会阻塞,直到另一方调用该通道的`receive()`方法。另一方屏蔽,直到发送方发送消息。在内部,这种实现与`QueueChannel`非常相似,只是它使用了`SynchronousQueue`(`BlockingQueue`的零容量实现)。这在发送方和接收方在不同线程中操作的情况下很好地工作,但是在队列中异步丢弃消息是不合适的。换句话说,对于`RendezvousChannel`,发送者知道某些接收者已经接受了该消息,而对于`QueueChannel`,该消息将被存储到内部队列中,并且可能永远不会被接收。 + +| |请记住,所有这些基于队列的通道在默认情况下仅在内存中存储消息。
当需要持久性时,你可以在“queue”元素中提供一个“message-store”属性来引用一个持久的`MessageStore`实现,或者你可以用一个持久代理支持的本地通道替换该本地通道,如 JMS 支持的通道或通道适配器,
后一种选项允许你利用任何 JMS 提供者的消息持久性实现,如[JMS 支持](./jms.html#jms)中所讨论的,
但是,当不需要在队列中进行缓冲时,最简单的方法是依赖`DirectChannel`,在下一节中讨论。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +`RendezvousChannel`对于实现请求-回复操作也很有用。发送方可以创建`RendezvousChannel`的临时匿名实例,然后在构建`Message`时将其设置为“replychannel”头。在发送`Message`之后,发送者可以立即调用`receive`(可选地提供超时值),以便在等待答复`Message`时阻塞。这与 Spring 集成的许多请求-应答组件内部使用的实现非常相似。 + +##### [](#channel-implementations-directchannel)`DirectChannel` + +`DirectChannel`具有点对点语义,但在其他方面比前面描述的任何基于队列的通道实现更类似于`PublishSubscribeChannel`。它实现了`SubscribableChannel`接口,而不是`PollableChannel`接口,因此它直接将消息分派给订阅服务器。然而,作为点对点信道,它与`PublishSubscribeChannel`的不同之处在于,它将每个`Message`发送到一个订阅的`MessageHandler`。 + +除了是最简单的点对点通道选项外,它最重要的特性之一是,它使单个线程能够在通道的“两边”执行操作。例如,如果处理程序订阅了`DirectChannel`,那么在`send()`方法调用返回之前,向该通道发送`Message`将触发该处理程序的`handleMessage(Message)`方法的调用。 + +提供具有这种行为的通道实现的主要动机是支持必须跨越通道的事务,同时仍然受益于通道提供的抽象和松耦合。如果发送调用是在事务的范围内调用的,那么处理程序调用的结果(例如,更新数据库记录)将在确定该事务的最终结果(提交或回滚)中起到一定的作用。 + +| |由于`DirectChannel`是最简单的选项,并且不会增加调度和管理 Poller 线程所需的任何额外开销,因此它是 Spring 集成中的默认通道类型,
的一般思想是为应用程序定义通道,考虑其中哪些需要提供缓冲或限制输入,并将其修改为基于队列的`PollableChannels`,
同样,如果一个频道需要广播消息,它不应该是`DirectChannel`,而应该是`PublishSubscribeChannel`,
,稍后,我们展示了如何配置这些通道中的每一个。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +`DirectChannel`在内部委托给消息调度器以调用其订阅的消息处理程序,并且该调度器可以具有由`load-balancer`或`load-balancer-ref`属性(互斥)公开的负载平衡策略。消息调度器使用负载平衡策略来帮助确定当多个消息处理程序订阅同一通道时消息如何在消息处理程序之间分配。为了方便起见,`load-balancer`属性公开指向`LoadBalancingStrategy`的预先存在的实现的值的枚举。唯一可用的值是`round-robin`(旋转中处理程序之间的负载平衡)和`none`(对于希望显式禁用负载平衡的情况)。在将来的版本中可能会添加其他策略实现。然而,自版本 3.0 以来,你可以提供自己的`LoadBalancingStrategy`实现,并通过使用`load-balancer-ref`属性注入它,该属性应该指向实现`LoadBalancingStrategy`的 Bean,如下例所示: + +a`FixedSubscriberChannel`是一个`SubscribableChannel`只支持一个不能取消订阅的`MessageHandler`订阅服务器的`SubscribableChannel`。当不涉及其他订阅服务器且不需要通道拦截器时,这对于高吞吐量性能用例非常有用。 + +``` + + + + + +``` + +注意,`load-balancer`和`load-balancer-ref`属性是互斥的。 + +负载平衡还与布尔`failover`属性一起工作。如果`failover`值为真(默认值),则当前面的处理程序抛出异常时,Dispatcher 将返回到任何后续的处理程序(根据需要)。顺序是由在处理程序本身上定义的可选订单值确定的,或者,如果不存在这样的值,则由处理程序订阅的顺序确定。 + +如果在特定的情况下,要求调度器总是尝试调用第一个处理程序,然后在每次发生错误时以相同的固定顺序返回,则不应提供负载平衡策略。换句话说,即使没有启用负载平衡,Dispatcher 仍然支持`failover`布尔属性。然而,在没有负载平衡的情况下,处理程序的调用总是根据它们的顺序从第一个开始。例如,当对小学、中学、大学等有一个明确的定义时,这种方法很有效。当使用名称空间支持时,任一端点上的`order`属性决定顺序。 + +| |请记住,负载平衡和`failover`仅在通道具有多个订阅消息处理程序时才适用。
在使用名称空间支持时,这意味着多个端点共享在`input-channel`属性中定义的相同通道引用。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 5.2 开始,当`failover`为真时,当前处理程序的失败以及失败的消息将分别记录在`debug`或`info`(如果已分别配置)下。 + +##### [](#executor-channel)`ExecutorChannel` + +`ExecutorChannel`是一个点对点通道,支持与`DirectChannel`相同的 Dispatcher 配置(负载平衡策略和`failover`布尔属性)。这两种调度通道类型之间的主要区别是,`ExecutorChannel`委托给`TaskExecutor`的一个实例来执行调度。这意味着发送方法通常不会阻塞,但也意味着处理程序调用可能不会发生在发送方的线程中。因此,它不支持跨越发送者和接收处理程序的事务。 + +| |例如,当使用带有抑制客户机的拒绝策略(例如`ThreadPoolExecutor.CallerRunsPolicy`)的`TaskExecutor`时,发送方有时会阻塞.,在线程池达到最大容量且执行者的工作队列已满的任何时候,发送者的线程都可以执行该方法。
由于这种情况只会以不可预测的方式发生,因此不应依赖它进行事务。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#flux-message-channel)`FluxMessageChannel` + +`FluxMessageChannel`是`org.reactivestreams.Publisher`实现的`org.reactivestreams.Publisher`,用于将消息发送到内部`reactor.core.publisher.Flux`,以供下游的响应订阅者按需消费。该通道实现既不是`SubscribableChannel`,也不是`PollableChannel`,因此只有`org.reactivestreams.Subscriber`实例可以用于从该通道消耗具有背压特性的反应流。另一方面,`FluxMessageChannel`实现了`ReactiveStreamsSubscribableChannel`及其`subscribeTo(Publisher>)`契约,允许接收来自反应源发布者的事件,将反应流连接到集成流。为了在整个集成流程中实现完全无反应的行为,必须在流程中的所有端点之间放置这样的通道。 + +有关与反应流的交互的更多信息,请参见[反应流支持](./reactive-streams.html#reactive-streams)。 + +##### [](#channel-implementations-threadlocalchannel)作用域信道 + +Spring Integration1.0 提供了`ThreadLocalChannel`的实现,但自 2.0 起该实现已被移除。现在,处理相同需求的更一般的方法是将`scope`属性添加到通道中。属性的值可以是上下文中可用的作用域的名称。例如,在 Web 环境中,某些作用域是可用的,任何自定义作用域实现都可以在上下文中注册。下面的示例显示了应用于通道的线程本地作用域,包括作用域本身的注册: + +``` + + + + + + + + + + + +``` + +在前面的示例中定义的通道也在内部委托给一个队列,但是该通道绑定到当前线程,因此队列的内容也是类似地绑定的。这样,发送到该通道的线程以后可以接收这些相同的消息,但没有其他线程能够访问它们。虽然很少需要线程范围的通道,但在`DirectChannel`实例被用来强制执行单个线程操作但任何回复消息都应该发送到“终端”通道的情况下,它们可能是有用的。如果该终端通道是线程范围的,则原始发送线程可以从终端通道收集其回复。 + +现在,由于任何通道都可以被作用域定义,所以除了线程本地之外,你还可以定义自己的作用域。 + +#### [](#channel-interceptors)信道拦截器 + +消息传递体系结构的优势之一是能够提供公共行为,并以非侵入性的方式捕获有关通过系统的消息的有意义的信息。由于`Message`实例被发送到`MessageChannel`实例并从`MessageChannel`实例接收,这些通道提供了截获发送和接收操作的机会。下面的清单中显示了`ChannelInterceptor`策略接口,它为每个操作提供了方法: + +``` +public interface ChannelInterceptor { + + Message preSend(Message message, MessageChannel channel); + + void postSend(Message message, MessageChannel channel, boolean sent); + + void afterSendCompletion(Message message, MessageChannel channel, boolean sent, Exception ex); + + boolean preReceive(MessageChannel channel); + + Message postReceive(Message message, MessageChannel channel); + + void afterReceiveCompletion(Message message, MessageChannel channel, Exception ex); +} +``` + +在实现接口之后,使用通道注册拦截器只需要进行以下调用: + +``` +channel.addInterceptor(someChannelInterceptor); +``` + +返回`Message`实例的方法可以用于转换`Message`,也可以返回’null’以阻止进一步的处理(当然,任何方法都可以抛出`RuntimeException`)。同样,`preReceive`方法可以返回`false`以阻止接收操作继续进行。 + +| |请记住,`receive()`调用仅与`PollableChannels`相关,
实际上,`SubscribableChannel`接口甚至没有定义`receive()`方法。
的原因是,当`Message`被发送到`SubscribableChannel`时,它被直接发送到零个或多个订阅者,这取决于信道的类型(例如,
a`PublishSubscribeChannel`发送到其所有订阅者)。因此,只有当拦截器应用到
时,才调用`preReceive(…​)`、`afterReceiveCompletion(…​)`拦截器方法。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 集成还提供了[Wire Tap](https://www.enterpriseintegrationpatterns.com/WireTap.html)模式的实现方式。这是一个简单的拦截器,它在不改变现有流的情况下将`Message`发送到另一个信道。它对于调试和监视非常有用。一个例子如[Wire Tap](#channel-wiretap)所示。 + +因为很少需要实现所有的拦截器方法,所以接口提供了 no-op 方法(返回`void`方法没有代码,`Message`-返回方法返回`Message`为-is,而`boolean`方法返回`true`)。 + +| |拦截器方法的调用顺序取决于信道的类型。
如前所述,基于队列的信道是接收方法首先被拦截的唯一信道,
此外,发送和接收拦截之间的关系取决于单独的发送者和接收者线程的时间安排。
例如,如果接收者在等待消息时已经被阻止,则顺序可以如下:`preSend`,`preReceive`,`postReceive`,`postSend`但是,
,如果接收方在发送方在该通道上放置了消息并已返回之后进行轮询,则顺序如下:`preSend`,`postSend`(some-time-elapses),`preReceive`,`postReceive`。
在这种情况下经过的时间取决于许多因素,因此通常是不可预测的(实际上,接收可能永远不会发生)。
队列的类型也起到一定的作用(例如,会合与优先级)。,简而言之,
,除了`preSend`在`postSend`和`preReceive`在`postReceive`之前这一事实外,你不能依赖该顺序。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从 Spring Framework4.1 和 Spring Integration4.1 开始,`ChannelInterceptor`提供了新的方法:`afterSendCompletion()`和`afterReceiveCompletion()`。它们是在`send()' and 'receive()`调用后调用的,而不考虑引发的允许资源清理的任何异常。请注意,通道在`ChannelInterceptor`列表中以与初始值`preSend()`和`preReceive()`调用相反的顺序调用这些方法。 + +从版本 5.1 开始,全局信道拦截器现在应用于动态注册的信道-例如,当使用 爪哇 DSL 时,通过使用`beanFactory.initializeBean()`或`IntegrationFlowContext`初始化的 bean。以前,在刷新应用程序上下文后创建 bean 时,不会应用拦截器。 + +此外,从版本 5.1 开始,当没有接收到消息时,将不再调用`ChannelInterceptor.postReceive()`;不再需要检查`null``Message`。以前,这种方法被称为。如果你的拦截器依赖于前面的行为,那么可以实现`afterReceiveCompleted()`,因为无论是否接收到消息,该方法都会被调用。 + +| |从版本 5.2 开始, Spring 消息传递模块中的`ChannelInterceptorAware`被弃用,而支持`InterceptableChannel`,它现在对其进行了扩展,以实现向后兼容。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#channel-template)`MessagingTemplate` + +Spring 当引入端点及其各种配置选项时,集成为消息传递组件提供了一个基础,该组件允许从消息传递系统非侵入性地调用你的应用程序代码。但是,有时需要从应用程序代码中调用消息传递系统。 Spring 在实现这样的用例时,为了方便起见,集成提供了一个`MessagingTemplate`,它支持跨消息通道的各种操作,包括请求和应答场景。例如,可以发送请求并等待答复,如下所示: + +``` +MessagingTemplate template = new MessagingTemplate(); + +Message reply = template.sendAndReceive(someChannel, new GenericMessage("test")); +``` + +在前面的示例中,模板将在内部创建一个临时匿名通道。模板上也可以设置“sendtimeout”和“receiveTimeout”属性,还支持其他交换类型。下面的清单显示了这些方法的签名: + +``` +public boolean send(final MessageChannel channel, final Message message) { ... +} + +public Message sendAndReceive(final MessageChannel channel, final Message request) { ... +} + +public Message receive(final PollableChannel channel) { ... +} +``` + +| |在[Enter the`GatewayProxyFactoryBean`](./gateway.html#gateway-proxy)中描述了一种侵入性较小的方法,它允许你调用带有有效负载或头值的简单接口,而不是`Message`实例。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#channel-configuration)配置消息通道 + +要创建消息通道实例,可以使用``元素表示 XML,也可以使用`DirectChannel`实例表示 爪哇 配置,如下所示: + +爪哇 + +``` +@Bean +public MessageChannel exampleChannel() { + return new DirectChannel(); +} +``` + +XML + +``` + +``` + +当使用不带任何子元素的``元素时,它将创建一个`DirectChannel`实例(a`SubscribableChannel`)。 + +要创建发布-订阅通道,请使用``元素(在 爪哇 中是`PublishSubscribeChannel`),如下所示: + +爪哇 + +``` +@Bean +public MessageChannel exampleChannel() { + return new PublishSubscribeChannel(); +} +``` + +XML + +``` + +``` + +你可以替代地提供各种``子元素来创建任何可对应信道类型(如[消息通道实现](#channel-implementations)中所描述的)。下面的部分展示了每种通道类型的示例。 + +##### [](#channel-configuration-directchannel)`DirectChannel`配置 + +如前所述,`DirectChannel`是默认类型。下面的清单显示了谁来定义一个: + +爪哇 + +``` +@Bean +public MessageChannel directChannel() { + return new DirectChannel(); +} +``` + +XML + +``` + +``` + +默认通道具有循环负载均衡器,并且还启用了故障转移(有关更多详细信息,请参见[`DirectChannel`](#channel-implementations-directchannel))。要禁用其中的一个或两个,请添加``子元素(`LoadBalancingStrategy``DirectChannel`的构造函数)并按以下方式配置属性: + +爪哇 + +``` +@Bean +public MessageChannel failFastChannel() { + DirectChannel channel = new DirectChannel(); + channel.setFailover(false); + return channel; +} + +@Bean +public MessageChannel failFastChannel() { + return new DirectChannel(null); +} +``` + +XML + +``` + + + + + + + +``` + +##### [](#channel-datatype-channel)数据类型通道配置 + +有时,使用者只能处理特定类型的有效负载,这迫使你确保输入消息的有效负载类型。首先想到的可能是使用消息过滤器。然而,消息过滤器所能做的就是过滤掉不符合使用者需求的消息。另一种方法是使用基于内容的路由器,将具有不兼容数据类型的消息路由到特定的转换器,以强制转换和转换到所需的数据类型。这可能行得通,但要实现同样的事情,一种更简单的方法是应用[数据类型通道](https://www.enterpriseintegrationpatterns.com/DatatypeChannel.html)模式。对于每个特定的有效负载数据类型,可以使用单独的数据类型通道。 + +要创建只接受包含特定有效负载类型的消息的数据类型通道,请在通道元素的`datatype`属性中提供数据类型的完全限定类名称,如下例所示: + +爪哇 + +``` +@Bean +public MessageChannel numberChannel() { + DirectChannel channel = new DirectChannel(); + channel.setDatatypes(Number.class); + return channel; +} +``` + +XML + +``` + +``` + +请注意,类型检查传递的是可分配给通道数据类型的任何类型。换句话说,前面示例中的`numberChannel`将接受有效负载为`java.lang.Integer`或`java.lang.Double`的消息。可以以逗号分隔的列表的形式提供多个类型,如下例所示: + +爪哇 + +``` +@Bean +public MessageChannel numberChannel() { + DirectChannel channel = new DirectChannel(); + channel.setDatatypes(String.class, Number.class); + return channel; +} +``` + +XML + +``` + +``` + +因此,前面示例中的“numberchannel”仅接受数据类型为`java.lang.Number`的消息。但是,如果消息的有效负载不是所需的类型,会发生什么情况?这取决于你是否定义了一个名为`integrationConversionService`的 Bean,它是 Spring 的[转换服务](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/validation.html#core-convert-ConversionService-API)的实例。如果不是,则将立即抛出`Exception`。但是,如果你已经定义了`integrationConversionService` Bean,那么将使用它来尝试将消息的有效负载转换为可接受类型。 + +你甚至可以注册自定义转换器。例如,假设你向上面配置的“numberchannel”发送了一条带有`String`有效负载的消息。你可以按以下方式处理此消息: + +``` +MessageChannel inChannel = context.getBean("numberChannel", MessageChannel.class); +inChannel.send(new GenericMessage("5")); +``` + +通常情况下,这将是一个完全合法的操作。然而,由于我们使用了数据类型通道,这样的操作的结果将产生类似于以下的异常: + +``` +Exception in thread "main" org.springframework.integration.MessageDeliveryException: +Channel 'numberChannel' +expected one of the following datataypes [class java.lang.Number], +but received [class java.lang.String] +… +``` + +出现异常是因为我们要求有效负载类型为`Number`,但是我们发送了`String`。因此,我们需要将`String`转换为`Number`。为此,我们可以实现类似于以下示例的转换器: + +``` +public static class StringToIntegerConverter implements Converter { + public Integer convert(String source) { + return Integer.parseInt(source); + } +} +``` + +然后,我们可以将其注册为集成转换服务的转换器,如下例所示: + +爪哇 + +``` +@Bean +@IntegrationConverter +public StringToIntegerConverter strToInt { + return new StringToIntegerConverter(); +} +``` + +XML + +``` + + + +``` + +或者在`StringToIntegerConverter`类中,当它被标记为用于自动扫描的`@Component`注释时。 + +解析“转换器”元素时,如果一个元素尚未定义,它将创建`integrationConversionService` Bean。有了该转换器,`send`操作现在就会成功,因为数据类型通道使用该转换器将`String`有效负载转换为`Integer`。 + +有关有效载荷类型转换的更多信息,请参见[有效载荷类型转换](./endpoint.html#payload-type-conversion)。 + +从版本 4.0 开始,`integrationConversionService`由`DefaultDatatypeChannelMessageConverter`调用,后者在应用程序上下文中查找转换服务。要使用不同的转换技术,可以在通道上指定`message-converter`属性。这必须是对`MessageConverter`实现的引用。只使用`fromMessage`方法。它为转换器提供了对消息头的访问(如果转换可能需要来自消息头的信息,例如`content-type`)。该方法只能返回转换后的有效载荷或完整的`Message`对象。如果是后者,转换器必须小心地从入站消息中复制所有的头。 + +或者,你可以声明一个 ID 为`datatypeChannelMessageConverter`的``类型的`MessageConverter`,并且该转换器被所有具有`datatype`的通道使用。 + +##### [](#channel-configuration-queuechannel)`QueueChannel`配置 + +要创建`QueueChannel`,请使用``子元素。你可以按以下方式指定频道的容量: + +爪哇 + +``` +@Bean +public PollableChannel queueChannel() { + return new QueueChannel(25); +} +``` + +XML + +``` + + + +``` + +| |如果你没有为这个``子元素上的“capacity”属性提供一个值,则生成的队列是无界的。
为了避免内存耗尽等问题,我们强烈建议你为有界队列设置一个显式的值。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +###### [](#persistent-queuechannel-configuration)持久`QueueChannel`配置 + +由于`QueueChannel`提供了缓冲消息的功能,但在默认情况下仅在内存中进行缓冲,因此它还引入了一种可能性,即在系统故障的情况下,消息可能会丢失。为了减轻这种风险,`QueueChannel`策略接口的持久实现可以支持`MessageGroupStore`策略。有关`MessageGroupStore`和`MessageStore`的更多详细信息,请参见[消息存储](./message-store.html#message-store)。 + +| |当使用`message-store`属性时,不允许使用`capacity`属性。| +|---|-----------------------------------------------------------------------------------| + +当`QueueChannel`接收到`Message`时,它将消息添加到消息存储中。当从`QueueChannel`对`Message`进行轮询时,它将从消息存储中删除。 + +默认情况下,`QueueChannel`将其消息存储在内存队列中,这可能导致前面提到的消息丢失场景。然而, Spring 集成提供了持久性存储,例如`JdbcChannelMessageStore`。 + +通过添加`message-store`属性,可以为任何`QueueChannel`配置消息存储,如下例所示: + +``` + + + + + + + + +``` + +(关于 爪哇/ Kotlin 配置选项,请参见下面的示例) + +Spring 集成 JDBC 模块还为许多流行的数据库提供了模式数据定义语言(DDL)。这些模式位于该模块(`spring-integration-jdbc`)的 org.springframework.integration.jdbc.store.channel 包中。 + +| |一个重要的特性是,对于任何事务持久存储(例如`JdbcChannelMessageStore`),只要 poller 配置了一个事务,从存储中删除的消息只有在事务成功完成的情况下才能被永久删除,
否则事务将回滚,而`Message`并没有丢失。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +随着越来越多的与“NoSQL”数据存储相关的 Spring 项目开始为这些存储提供底层支持,消息存储的许多其他实现都是可用的。如果找不到满足你特定需求的接口,你还可以提供你自己的`MessageGroupStore`接口实现。 + +自版本 4.0 以来,如果可能的话,我们建议将`QueueChannel`实例配置为使用`ChannelMessageStore`。与一般的消息存储相比,这些通常针对这种使用进行了优化。如果`ChannelMessageStore`是`ChannelPriorityMessageStore`,则在优先顺序内以 FIFO 方式接收消息。优先级的概念由消息存储实现确定。例如,下面的示例显示了[MongoDB 通道消息存储](./mongodb.html#mongodb-priority-channel-message-store)的 爪哇 配置: + +爪哇 + +``` +@Bean +public BasicMessageGroupStore mongoDbChannelMessageStore(MongoDbFactory mongoDbFactory) { + MongoDbChannelMessageStore store = new MongoDbChannelMessageStore(mongoDbFactory); + store.setPriorityEnabled(true); + return store; +} + +@Bean +public PollableChannel priorityQueue(BasicMessageGroupStore mongoDbChannelMessageStore) { + return new PriorityChannel(new MessageGroupQueue(mongoDbChannelMessageStore, "priorityQueue")); +} +``` + +爪哇 DSL + +``` +@Bean +public IntegrationFlow priorityFlow(PriorityCapableChannelMessageStore mongoDbChannelMessageStore) { + return IntegrationFlows.from((Channels c) -> + c.priority("priorityChannel", mongoDbChannelMessageStore, "priorityGroup")) + .... + .get(); +} +``` + +Kotlin DSL + +``` +@Bean +fun priorityFlow(mongoDbChannelMessageStore: PriorityCapableChannelMessageStore) = + integrationFlow { + channel { priority("priorityChannel", mongoDbChannelMessageStore, "priorityGroup") } + } +``` + +| |注意`MessageGroupQueue`class.
这是一个`BlockingQueue`实现来使用`MessageGroupStore`的操作。| +|---|---------------------------------------------------------------------------------------------------------------------------------------| + +定制`QueueChannel`环境的另一个选项是由``子元素的`ref`属性或其特定的构造函数提供的。此属性提供对任何`java.util.Queue`实现的引用。例如,分布式的 Hazelcast[`IQueue`](https://hazelcast.com/use-cases/imdg/imdg-messaging/)可以配置如下: + +``` +@Bean +public HazelcastInstance hazelcastInstance() { + return Hazelcast.newHazelcastInstance(new Config() + .setProperty("hazelcast.logging.type", "log4j")); +} + +@Bean +public PollableChannel distributedQueue() { + return new QueueChannel(hazelcastInstance() + .getQueue("springIntegrationQueue")); +} +``` + +##### [](#channel-configuration-pubsubchannel)`PublishSubscribeChannel`配置 + +要创建`PublishSubscribeChannel`,请使用 \元素。当使用这个元素时,你还可以指定用于发布消息的`task-executor`(如果没有指定消息,它将在发送方的线程中发布),如下所示: + +爪哇 + +``` +@Bean +public MessageChannel pubsubChannel() { + return new PublishSubscribeChannel(someExecutor()); +} +``` + +XML + +``` + +``` + +如果在`PublishSubscribeChannel`的下游提供一个重序列器或聚合器,则可以将通道上的’apply-sequence’属性设置为`true`。这样做表明通道在传递消息之前应该设置`sequence-size`和`sequence-number`消息头以及相关 ID。例如,如果有五个订阅服务器,`sequence-size`将被设置为`5`,并且消息将具有`sequence-number`标头值,范围从`1`到`5`。 + +除了`Executor`之外,还可以配置`ErrorHandler`。默认情况下,`PublishSubscribeChannel`使用`MessagePublishingErrorHandler`实现从`errorChannel`头发送一个错误到`MessageChannel`或全局`errorChannel`实例。如果不配置`Executor`,则忽略`ErrorHandler`,并将异常直接抛出到调用者的线程。 + +如果在`PublishSubscribeChannel`的下游提供`Resequencer`或`Aggregator`,则可以将通道上的’apply-sequence’属性设置为`true`。这样做表明,在传递消息之前,通道应该设置序列大小和序列号消息头以及相关 ID。例如,如果有五个订阅服务器,序列大小将设置为`5`,并且消息将具有从`1`到`5`的序列号标头值。 + +下面的示例展示了如何将`apply-sequence`标头设置为`true`: + +爪哇 + +``` +@Bean +public MessageChannel pubsubChannel() { + PublishSubscribeChannel channel = new PublishSubscribeChannel(); + channel.setApplySequence(false); + return channel; +} +``` + +XML + +``` + +``` + +| |默认情况下,`apply-sequence`值是`false`,这样,发布-订阅通道就可以向多个出站通道发送完全相同的消息实例。,
自 Spring 集成以来,当标记设置为`true`时,强制执行有效负载和头引用的不可变性,该通道创建新的`Message`实例,其有效负载引用相同,但标头值不同。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 5.4.3 开始,`PublishSubscribeChannel`还可以配置其`requireSubscribers`选项的`requireSubscribers`,以指示此通道在没有订阅服务器时不会静默忽略消息。当没有订阅者时,将抛出带有`MessageDispatchingException`消息的`Dispatcher has no subscribers`消息,并将此选项设置为`true`。 + +##### [](#channel-configuration-executorchannel)`ExecutorChannel` + +要创建`ExecutorChannel`,请添加带有`task-executor`属性的``子元素。该属性的值可以引用上下文中的任何`TaskExecutor`。例如,这样做可以配置线程池,用于将消息分配给订阅的处理程序。如前所述,这样做会破坏发送方和接收方之间的单线程执行上下文,从而处理程序的调用不会共享任何活动事务上下文(即,处理程序可能抛出`Exception`,但`send`调用已成功返回)。下面的示例展示了如何使用`dispatcher`元素并在`task-executor`属性中指定一个执行器: + +爪哇 + +``` +@Bean +public MessageChannel executorChannel() { + return new ExecutorChannel(someExecutor()); +} +``` + +XML + +``` + + + +``` + +| |`load-balancer`和`failover`选项在 \子元素上也是可用的,如前面在[`DirectChannel`配置](#channel-configuration-directchannel)中所述。
同样的默认值也适用,因此,
,通道具有循环负载平衡策略,启用了故障转移,除非为其中一个或两个属性提供了显式配置,如以下示例所示:

```



```| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#channel-configuration-prioritychannel)`PriorityChannel`配置 + +要创建`PriorityChannel`,请使用``子元素,如下例所示: + +爪哇 + +``` +@Bean +public PollableChannel priorityChannel() { + return new PriorityChannel(20); +} +``` + +XML + +``` + + + +``` + +默认情况下,通道查询消息的`priority`头。但是,你可以提供一个自定义的`Comparator`引用。另外,请注意`PriorityChannel`(与其他类型一样)确实支持`datatype`属性。与`QueueChannel`一样,它也支持`capacity`属性。下面的示例演示了所有这些: + +Java + +``` +@Bean +public PollableChannel priorityChannel() { + PriorityChannel channel = new PriorityChannel(20, widgetComparator()); + channel.setDatatypes(example.Widget.class); + return channel; +} +``` + +XML + +``` + + + +``` + +自版本 4.0 以来,`priority-channel`子元素支持`message-store`选项(在这种情况下不允许`comparator`和`capacity`)。消息存储必须是`PriorityCapableChannelMessageStore`。`PriorityCapableChannelMessageStore`的实现方式目前提供给`Redis`、`JDBC`和`MongoDB`。有关更多信息,请参见[`QueueChannel`Configuration]和[消息存储](./message-store.html#message-store)。你可以在[支持消息通道](./jdbc.html#jdbc-message-store-channels)中找到示例配置。 + +##### [](#channel-configuration-rendezvouschannel)`RendezvousChannel`配置 + +当 queue 子元素是``时,将创建`RendezvousChannel`。它没有为前面描述的那些提供任何额外的配置选项,并且它的队列不接受任何容量值,因为它是一个零容量直接切换队列。下面的示例展示了如何声明`RendezvousChannel`: + +Java + +``` +@Bean +public PollableChannel rendezvousChannel() { + return new RendezvousChannel(); +} +``` + +XML + +``` + + + +``` + +##### [](#channel-configuration-threadlocalchannel)作用域信道配置 + +任何通道都可以配置`scope`属性,如下例所示: + +``` + +``` + +##### [](#channel-configuration-interceptors)信道拦截器配置 + +消息通道也可以具有拦截器,如[信道拦截器](#channel-interceptors)中所述。可以将``子元素添加到``(或更具体的元素类型)中。你可以提供`ref`属性来引用实现`ChannelInterceptor`接口的任何 Spring 托管对象,如下例所示: + +``` + + + + + +``` + +通常,我们建议在单独的位置定义拦截器实现,因为它们通常提供可以跨多个通道重用的公共行为。 + +##### [](#global-channel-configuration-interceptors)全局信道拦截器配置 + +通道拦截器为每个通道应用横切行为提供了一种简洁的方法。如果应该在多个信道上应用相同的行为,那么为每个信道配置相同的拦截器集合将不是最有效的方法。 Spring 为了避免重复配置,同时也使拦截器能够应用于多个信道,集成提供了全局拦截器。考虑以下两个例子: + +``` + + + +``` + +``` + + + +``` + +每个``元素都允许你定义一个全局拦截器,它应用于所有与`pattern`属性定义的任何模式匹配的通道上。在前一种情况下,全局拦截器应用于“Thing1”通道和所有其他以“Thing2”或“Input”开头的通道,但不应用于以“Thing3”开头的通道(自版本 5.0 以来)。 + +| |将此语法添加到模式中会导致一个可能的(尽管可能不太可能)问题,
如果你有一个名为`!thing1`的 Bean 模式,并且你在通道拦截器的`!thing1`模式中包含了一个`pattern`模式,它不再匹配。
模式现在匹配所有未命名`thing1`的 bean。
在这种情况下,你可以在带有`\`的模式中转义`!`。
模式`\!thing1`匹配名为`!thing1`的 Bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Order 属性允许你在给定信道上有多个拦截器时管理此拦截器被注入的位置。例如,通道“InputChannel”可以在本地配置单独的拦截器(见下文),如下例所示: + +``` + + + + + +``` + +一个合理的问题是“相对于本地配置的其他拦截器或通过其他全球拦截器定义配置的其他拦截器,如何注入一个全球拦截器?”当前的实现提供了一种定义拦截器执行顺序的简单机制。`order`属性中的正数确保在任何现有拦截器之后注入拦截器,而负数确保在现有拦截器之前注入拦截器。这意味着,在前面的示例中,全局拦截器是在本地配置的’wire-tap’拦截器之后注入的(因为其`order`大于`0`)。如果存在另一个匹配`pattern`的全局拦截器,其顺序将通过比较两个拦截器的`order`属性的值来确定。要在现有拦截器之前注入一个全局拦截器,请使用`order`属性的负值。 + +| |注意,`order`和`pattern`属性都是可选的。
`order`的默认值将为 0,而`pattern`的默认值为’\*’(以匹配所有通道)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#channel-wiretap)丝锥 + +正如前面提到的, Spring 集成提供了一种简单的线分接拦截器。你可以在``元素内的任何通道上配置线控分接。这样做对于调试特别有用,并且可以与 Spring Integration 的日志通道适配器结合使用,如下所示: + +``` + + + + + + + +``` + +| |“logging-channel-adapter”还接受一个“expression”属性,这样你就可以根据“payload”和“headers”变量计算 SPEL 表达式。,
或者,要记录完整的消息`toString()`结果,请为“log-full-message”属性提供一个`true`的值,默认情况下,
,它是`false`,这样只记录有效负载。
将其设置为`true`,可以记录除有效负载之外的所有头。
“表达式”选项提供了最大的灵活性(例如,`expression="payload.user.name"`)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +关于线导和其他类似组件([消息发布配置](./message-publishing.html#message-publishing-config))的一个常见误解是,它们本质上是自动异步的。默认情况下,线接作为一个组件不会被异步调用。 Spring 相反,集成关注于配置异步行为的单一统一方法:消息通道。使消息流的某些部分同步或异步的是在该流中配置的消息通道的类型。这是消息通道抽象的主要好处之一。从框架的一开始,我们就一直强调消息通道作为框架的一流公民的需求和价值。它不只是 EIP 模式的内在的、隐式的实现。它作为可配置组件完全公开给最终用户。因此,线抽头组件只负责执行以下任务: + +* 通过点击一个通道(例如`channelA`)来拦截消息流。 + +* 抓住每条消息 + +* 将消息发送到另一个通道(例如,`channelB`) + +它本质上是桥模式的一种变体,但它被封装在一个通道定义中(因此更容易启用和禁用,而不会破坏流)。此外,与 Bridge 不同的是,它基本上是分叉另一个消息流。这个流是同步的还是异步的?答案取决于“ChannelB”是什么类型的消息通道。我们有以下几种选择:直接通道、可检索通道和执行器通道。后两种方法打破了线程边界,使得在这些通道上的通信是异步的,因为将消息从该通道发送到其订阅的处理程序的任务发生在不同的线程上,而不是用于将消息发送到该通道的线程上。这就是什么将使你的线接流同步或异步。它与框架中的其他组件(例如 Message Publisher)保持一致,并通过避免你提前担心(除了编写线程安全代码)特定代码块应该实现为同步还是异步,从而增加了一定程度的一致性和简单性。两段代码(例如,组件 A 和组件 B)在消息通道上的实际连接使它们的协作是同步的或异步的。将来,你甚至可能想要从同步转换为异步,而 Message Channel 允许你在不接触代码的情况下快速地执行此操作。 + +关于线接的最后一点是,尽管上面提供了默认情况下不是异步的理由,但你应该记住,通常希望尽快传递消息。因此,使用异步通道选项作为线控分路器的出站通道将是非常常见的。然而,在默认情况下,我们并不强制执行异步行为。如果我们这样做的话,有许多用例将会被打破,包括你可能不希望打破事务边界。也许你出于审计目的而使用了线接模式,并且你确实希望在原始事务中发送审计消息。例如,你可以将线接连接到一个 JMS 出站通道适配器。这样,你就能两全其美:1)JMS 消息的发送可能发生在事务中,而 2)它仍然是一个“触发并忘记”操作,从而防止主消息流中出现任何明显的延迟。 + +| |从 4.0 版本开始,当拦截器(例如[`WireTap`类](https://DOCS. Spring.io/autorepo/DOCS/ Spring-integration/current/api/org/springframework/integration/channel/interceptor/wiretap.html)引用信道时,避免循环引用是很重要的。
你需要从当前拦截器拦截的信道中排除此类信道。
如果你有自定义的
,可以使用适当的模式或编程地完成此操作。如果你有一个自定义的你还可以在拦截器方法中添加运行时保护,以确保信道不是拦截器引用的信道。
`WireTap`使用了这两种技术。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +从版本 4.3 开始,`WireTap`有额外的构造函数,它们接受`channelName`而不是`MessageChannel`实例。这对于 Java 配置和使用通道自动创建逻辑时都很方便。目标`MessageChannel` Bean 是从提供的`channelName`以后在与拦截器的第一次交互中解析出来的。 + +| |通道解析需要`BeanFactory`,因此有线分接实例必须是 Spring 管理的 Bean。| +|---|----------------------------------------------------------------------------------------------------| + +这种后期绑定方法还允许使用 Java DSL 配置简化典型的接线模式,如下例所示: + +``` +@Bean +public PollableChannel myChannel() { + return MessageChannels.queue() + .wireTap("loggingFlow.input") + .get(); +} + +@Bean +public IntegrationFlow loggingFlow() { + return f -> f.log(); +} +``` + +##### [](#conditional-wiretap)条件式电线分接 + +通过使用`selector`或`selector-expression`属性,可以使电线分接成为有条件的。`selector`引用了`MessageSelector` Bean,这可以在运行时确定消息是否应该转到 TAP 通道。类似地,`selector-expression`是一个执行相同目的的布尔 SPEL 表达式:如果表达式的计算结果为`true`,则消息将被发送到 TAP 通道。 + +##### [](#channel-global-wiretap)全局线接接头配置 + +作为[全局信道拦截器配置](#global-channel-configuration-interceptors)的特殊情况,可以配置全局线接。为此,配置一个顶级`wire-tap`元素。现在,除了正常的`wire-tap`名称空间支持外,`pattern`和`order`属性也得到了支持,并且它们的工作方式与`channel-interceptor`完全相同。下面的示例展示了如何配置全局线接: + +Java + +``` +@Bean +@GlobalChannelInterceptor(patterns = "input*,thing2*,thing1", order = 3) +public WireTap wireTap(MessageChannel wiretapChannel) { + return new WireTap(wiretapChannel); +} +``` + +XML + +``` + +``` + +| |全局线接提供了一种方便的方式,可以在不修改现有通道配置的情况下,在外部配置单通道线接。
要这样做,请将`pattern`属性设置为目标通道名。
例如,你可以使用此技术来配置测试用例,以验证通道上的消息。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#channel-special-channels)特殊频道 + +默认情况下,在应用程序上下文中定义了两个特殊通道:`errorChannel`和`nullChannel`。“nullchannel”(`NullChannel`的实例)的作用类似于`/dev/null`,记录在`DEBUG`级别发送到它的任何消息并立即返回。对`org.reactivestreams.Publisher`发送的消息的有效负载应用了特殊处理:它在此信道中被立即订阅,以启动反应流处理,尽管数据被丢弃。从反应流处理中抛出的错误(参见`Subscriber.onError(Throwable)`)将记录在警告级别下,以进行可能的调查。如果需要对这样的错误做任何事情,则`[ReactiveRequestHandlerAdvice](./handler-advice.html#reactive-advice)`具有`Mono.doOnError()`自定义的消息处理程序可以应用于产生`Mono`的消息处理程序,对此`nullChannel`进行回复。当你遇到不关心的通道解析错误时,你可以将受影响组件的`output-channel`属性设置为“nullchannel”(名称“nullchannel”在应用程序上下文中保留)。 + +“errorchannel”在内部用于发送错误消息,并且可能会被自定义配置覆盖。这在[错误处理](./error-handling.html#error-handling)中有更详细的讨论。 + +有关消息通道和拦截器的更多信息,请参见 Java DSL 章节中的[消息通道](./dsl.html#java-dsl-channels)。 + +### [](#polling-consumer)poller + +本节描述了 Spring 集成中轮询的工作方式。 + +#### [](#polling-consumer-2)轮询消费者 + +当消息端点(通道适配器)连接到通道并进行实例化时,它们会产生以下实例之一: + +* [`PollingConsumer`](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/endpoint/pollingconsumer.html) + +* [`EventDrivenConsumer`](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/endpoint/eventdrivenconsumer.html) + +实际的实现取决于这些端点连接到的通道类型。与实现[`org.springframework.messaging.SubscribableChannel`](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/index.html?org/springframework/messing/subscribablechannel.html)接口的通道适配器产生`EventDrivenConsumer`的实例。另一方面,与实现了[`org.springframework.messaging.PollableChannel`](https://DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/index.html?org/springframework/messaging/pollablechannel.html)接口的通道适配器(例如`QueueChannel`)产生了`PollingConsumer`的实例。 + +轮询消费者让 Spring 集成组件主动轮询消息,而不是以事件驱动的方式处理消息。 + +在许多消息传递场景中,它们代表了一个关键的交叉问题。在 Spring 集成中,轮询消费者是基于具有相同名称的模式,这在 Gregor Hohpe 和 Bobby Woolf 的书*Enterprise 整合模式*中进行了描述。你可以在[Book 的网站](https://www.enterpriseintegrationpatterns.com/PollingConsumer.html)上找到模式的描述。 + +#### [](#pollable-message-source)可选消息源 + +Spring 集成提供了轮询消费者模式的第二种变化。当使用入站通道适配器时,这些适配器通常用`SourcePollingChannelAdapter`包装。例如,当从远程 FTP 服务器位置检索消息时,[FTP 入站通道适配器](./ftp.html#ftp-inbound)中描述的适配器配置了一个 poller,以定期检索消息。因此,当组件配置为 Poller 时,生成的实例属于以下类型之一: + +* [`PollingConsumer`](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/endpoint/pollingconsumer.html) + +* [`SourcePollingChannelAdapter`](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/endpoint/sourcepollingchanneladapter.html) + +这意味着 Poller 在入站和出站消息传递场景中都会使用。以下是使用 Poller 的一些用例: + +* 轮询某些外部系统,例如 FTP 服务器、数据库和 Web 服务 + +* 轮询内部(可匹配)消息通道 + +* 轮询内部服务(例如在 Java 类上重复执行方法) + +| |AOP 建议类可以应用于 Pollers,在`advice-chain`中,例如事务通知来启动事务。,
从版本 4.1 开始,提供了`PollSkipAdvice`。
投票者使用触发器来确定下一次投票的时间。
`PollSkipAdvice`可以用来抑制(跳过)投票,这可能是因为存在一些下游条件,这将阻止消息被处理。
使用此建议,你必须为它提供一个`PollSkipStrategy`的实现。
从版本 4.2.5 开始,提供了一个`SimplePollSkipStrategy`,
要使用它,你可以将一个实例作为 Bean 添加到应用程序上下文中,将其注入到`PollSkipAdvice`中,并将其添加到 Poller 的建议链中。
要跳过轮询,请调用`skipPolls()`。
恢复轮询,请调用`reset()`。
4.2 版本在该区域增加了更多的灵活性。
参见[消息源的条件 Poller](#conditional-pollers)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +本章仅对轮询消费者以及他们如何适应消息通道(参见[消息通道](./channel.html#channel))和通道适配器(参见[通道适配器](./channel-adapter.html#channel-adapter))的概念进行了高级概述。有关一般消息传递端点和特别是轮询消费者的更多信息,请参见[消息端点](./endpoint.html#endpoint)。 + +#### [](#deferred-acks-message-source)延迟确认可收集消息源 + +从版本 5.0.1 开始,某些模块提供`MessageSource`实现,支持延迟确认,直到下游流完成(或将消息传递给另一个线程)。这目前仅限于`AmqpMessageSource`和`KafkaMessageSource`。 + +有了这些消息源,`IntegrationMessageHeaderAccessor.ACKNOWLEDGMENT_CALLBACK`报头(参见[`MessageHeaderAccessor`api](./message.html#message-header-accessor))被添加到消息中。当与可收集的消息源一起使用时,头的值是`AcknowledgmentCallback`的实例,如下例所示: + +``` +@FunctionalInterface +public interface AcknowledgmentCallback { + + void acknowledge(Status status); + + boolean isAcknowledged(); + + void noAutoAck(); + + default boolean isAutoAck(); + + enum Status { + + /** + * Mark the message as accepted. + */ + ACCEPT, + + /** + * Mark the message as rejected. + */ + REJECT, + + /** + * Reject the message and requeue so that it will be redelivered. + */ + REQUEUE + + } + +} +``` + +并非所有消息源(例如,a`KafkaMessageSource`)都支持`REJECT`状态。其处理方法与`ACCEPT`相同。 + +应用程序可以随时确认消息,如下例所示: + +``` +Message received = source.receive(); + +... + +StaticMessageHeaderAccessor.getAcknowledgmentCallback(received) + .acknowledge(Status.ACCEPT); +``` + +如果`MessageSource`被连接到`SourcePollingChannelAdapter`,那么当下游流程完成后,Poller 线程返回适配器时,适配器将检查确认是否已经被确认,如果没有,将其状态设置为`ACCEPT`it(如果流抛出异常,则设置`REJECT`)。状态值是在[`AcknowledgmentCallback.Status`枚举](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/support/concredgmentcallback.status.html)中定义的。 + +Spring 集成提供了`MessageSourcePollingTemplate`来执行对`MessageSource`的临时轮询。这也需要在`MessageHandler`回调返回(或抛出异常)时在`AcknowledgmentCallback`上设置`ACCEPT`或`REJECT`。下面的示例展示了如何使用`MessageSourcePollingTemplate`进行轮询: + +``` +MessageSourcePollingTemplate template = + new MessageSourcePollingTemplate(this.source); +template.poll(h -> { + ... +}); +``` + +在这两种情况下(`SourcePollingChannelAdapter`和`MessageSourcePollingTemplate`),都可以通过在回调中调用`noAutoAck()`来禁用 auto ack/nack。如果你将消息传递给另一个线程并希望稍后确认,那么你可能会这样做。并不是所有的实现都支持这一点(例如,Apache Kafka 不支持这一点,因为偏移提交必须在同一个线程上执行)。 + +#### 消息源的[](#conditional-pollers)条件 Pollers + +本节介绍如何使用条件 Pollers。 + +##### [](#background)背景 + +`Advice`对象,在 poller 上的`advice-chain`中,为整个轮询任务(消息检索和处理)提供建议。这些“周围建议”方法无法访问投票的任何上下文——只有投票本身。这对于需求很好,比如执行事务任务或由于某些外部条件而跳过轮询,正如前面讨论的那样。如果我们希望根据轮询的`receive`部分的结果采取某些操作,或者如果我们希望根据条件调整 Poller,该怎么办?在这些情况下, Spring 集成提供了“智能”轮询。 + +##### [](#smart-polling)“智能”民调 + +5.3 版引入了`ReceiveMessageAdvice`接口。(在`MessageSourceMutator`中,`AbstractMessageSourceAdvice`已被弃用,而支持`default`方法。)在`advice-chain`中实现此接口的任何`Advice`对象仅应用于接收操作-`MessageSource.receive()`和`PollableChannel.receive(timeout)`。因此,它们只能应用于`SourcePollingChannelAdapter`或`PollingConsumer`。这样的类实现了以下方法: + +* `beforeReceive(Object source)`在`Object.receive()`方法之前调用该方法。它允许你检查和重新配置源代码。返回`false`将取消此轮询(类似于前面提到的`PollSkipAdvice`)。 + +* `Message afterReceive(Message result, Object source)`该方法是在`receive()`方法之后调用的。同样,你可以重新配置源代码或采取任何操作(可能取决于结果,如果源代码没有创建消息,结果可能是`null`)。你甚至可以返回一条不同的消息。 + +| |线程安全

如果一个建议会使结果发生变异,那么你不应该使用`TaskExecutor`来配置 poller。
如果一个建议会使源发生变异,那么这种变异就不是线程安全的,可能会导致意外的结果,特别是对于高频的 poller,
如果你需要同时处理轮询结果,考虑使用下游`ExecutorChannel`,而不是向 poller 添加执行器。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |建议链排序

你应该了解在初始化期间如何处理建议链,`Advice`不实现`ReceiveMessageAdvice`的对象将应用于整个轮询过程,并且所有这些对象都将首先被调用,在任何`ReceiveMessageAdvice`.
之前,`ReceiveMessageAdvice`对象是按照围绕源`receive()`方法的顺序被调用的。
如果你有,例如,`Advice`对象`a, b, c, d`,其中`b`和`d`是,则这些对象也按照以下顺序应用:`a, c, b, d`。如果源已经是`Proxy`,则在任何现有的`Advice`对象之后调用`ReceiveMessageAdvice`。
如果你希望更改顺序,则必须自己连接代理。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#simpleactiveidlereceivemessageadvice)`SimpleActiveIdleReceiveMessageAdvice` + +(以前的`SimpleActiveIdleMessageSourceAdvice`仅用于`MessageSource`已不推荐。)此建议是`ReceiveMessageAdvice`的一个简单实现。当与`DynamicPeriodicTrigger`结合使用时,它会调整轮询频率,这取决于上一次轮询是否导致了消息。Poller 还必须具有对相同`DynamicPeriodicTrigger`的引用。 + +| |重要提示:异步切换

`SimpleActiveIdleReceiveMessageAdvice`基于`receive()`结果修改触发器。
只有在 poller 线程上调用通知时,此操作才有效。
它不工作如果 poller 有`task-executor`.
来使用此建议,其中你希望在轮询结果之后使用异步操作,请稍后进行异步切换,也许可以使用`ExecutorChannel`。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#compoundtriggeradvice)`CompoundTriggerAdvice` + +此建议允许根据投票是否返回消息来选择两个触发器中的一个。考虑一个使用`CronTrigger`的 poller。`CronTrigger`实例是不可变的,因此一旦构造它们,就不能对它们进行更改。考虑一个用例,我们希望使用 CRON 表达式每小时触发一次轮询,但是如果没有收到消息,则每分钟轮询一次,并且在检索到消息时,恢复为使用 CRON 表达式。 + +建议(和 Poller)为此目的使用`CompoundTrigger`。触发器的`primary`触发器可以是`CronTrigger`。当通知检测到没有收到消息时,它会将辅助触发器添加到`CompoundTrigger`。当`CompoundTrigger`实例的`nextExecutionTime`方法被调用时,如果存在,它将委托给辅助触发器。否则,它将委托给主触发器。 + +Poller 还必须具有对相同`CompoundTrigger`的引用。 + +下面的示例显示了每小时 CRON 表达式的配置,并将其退回到每分钟: + +``` + + + + + + + + + + + + + + + + + + + + + + + +``` + +| |重要提示:异步切换

`CompoundTriggerAdvice`基于`receive()`结果修改触发器。
只有在 poller 线程上调用通知时,此操作才有效。
它不工作如果 poller 有`task-executor`。
使用此建议,其中你希望在轮询结果之后使用异步操作,请稍后进行异步切换,也许可以使用`ExecutorChannel`。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### [](#messagesource-only-advices)MessageSource-仅限建议 + +有些建议可能只适用于`MessageSource.receive()`,而对于`PollableChannel`则没有意义。为此,仍然存在`MessageSourceMutator`接口(`ReceiveMessageAdvice`的扩展)。对于`default`方法,它完全替换了已经废弃的`AbstractMessageSourceAdvice`方法,并且应该在只期望`MessageSource`代理的实现中使用。有关更多信息,请参见[入站通道适配器:轮询多个服务器和目录](./ftp.html#ftp-rotating-server-advice)。 + +### [](#channel-adapter)通道适配器 + +通道适配器是一个消息端点,它允许将单个发送方或接收方连接到消息通道。 Spring 集成提供了许多适配器以支持各种传输,例如 JMS、文件、HTTP、Web 服务、邮件等。本参考指南的下几章将讨论每个适配器。然而,这一章的重点是简单但灵活的方法-调用通道适配器支持。这里既有入站适配器,也有出站适配器,每个适配器都可以配置为使用核心名称空间中提供的 XML 元素。这些提供了一种扩展 Spring 集成的简单方法,只要你有一个可以作为源或目标调用的方法。 + +#### [](#channel-adapter-namespace-inbound)配置入站通道适配器 + +一个`inbound-channel-adapter`元素(在 Java 配置中是`SourcePollingChannelAdapter`)可以在 Spring 管理的对象上调用任何方法,并在将方法的输出转换为`Message`之后,将一个非空返回值发送到`MessageChannel`。当适配器的订阅被激活时,Poller 尝试从源接收消息。根据提供的配置,用`TaskScheduler`调度 Poller。要为单个通道适配器配置轮询间隔或 CRON 表达式,可以提供具有调度属性之一的“poller”元素,例如“fixed-rate”或“CRON”。下面的示例定义了两个`inbound-channel-adapter`实例: + +Java DSL + +``` +@Bean +public IntegrationFlow source1() { + return IntegrationFlows.from(() -> new GenericMessage<>(...), + e -> e.poller(p -> p.fixedRate(5000))) + ... + .get(); +} + +@Bean +public IntegrationFlow source2() { + return IntegrationFlows.from(() -> new GenericMessage<>(...), + e -> e.poller(p -> p.cron("30 * 9-17 * * MON-FRI"))) + ... + .get(); +} +``` + +Java + +``` +public class SourceService { + + @InboundChannelAdapter(channel = "channel1", poller = @Poller(fixedRate = "5000")) + Object method1() { + ... + } + + @InboundChannelAdapter(channel = "channel2", poller = @Poller(cron = "30 * 9-17 * * MON-FRI")) + Object method2() { + ... + } +} +``` + +Kotlin DSL + +``` +@Bean +fun messageSourceFlow() = + integrationFlow( { GenericMessage<>(...) }, + { poller { it.fixedRate(5000) } }) { + ... + } +``` + +XML + +``` + + + + + + + +``` + +另见[通道适配器表达式和脚本](#channel-adapter-expressions-and-scripts)。 + +| |如果没有提供 Poller,则必须在上下文中注册一个默认 Poller。
有关更多详细信息,请参见[端点命名空间支持](./endpoint.html#endpoint-namespace)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |重要:poller 配置

所有`inbound-channel-adapter`类型都支持`SourcePollingChannelAdapter`,这意味着它们包含一个 poller 配置,该配置轮询`MessageSource`(以调用产生该值的自定义方法)这将成为基于 poller 中指定的配置的`Message`有效负载)。
下面的示例显示了两个 poller 的配置:


在第一个配置中,轮询任务每轮询一次被调用,并且在每个任务(轮询)期间,根据`max-messages-per-poll`属性值调用该方法(该方法将产生消息)一次,
在第二个配置中,轮询任务将在每个轮询中调用 10 次,或者直到它返回’null’为止,因此,在每次轮询以一秒的间隔进行时,每次轮询可能会产生 10 条消息。
但是,如果配置看起来像以下示例,会发生什么情况:

```

```
请注意,没有`max-messages-per-poll`指定。
,我们将在后面介绍,在`PollingConsumer`(例如,`service-activator`,`filter`,`router`,以及其他)中,相同的 poller 配置将对`-1`的`max-messages-per-poll`具有默认值,这意味着“不停地执行轮询任务,除非轮询方法返回 null(可能是因为`QueueChannel`中没有更多的消息)”,然后睡眠一秒。,

但是,在`SourcePollingChannelAdapter`中,有点不同。
`max-messages-per-poll`的默认值是`1`,除非你显式地将其设置为负值(例如`-1`)。
这可以确保 poller 可以对生命周期事件(例如开始和停止)做出反应,并防止它可能在无限循环中旋转如果`MessageSource`的自定义方法的实现可能永远不会返回 null,并且碰巧是不可中断的。

但是,如果你确信你的方法可以返回 null,并且你需要对每个轮询中可用的尽可能多的源进行轮询,你应该显式地将`max-messages-per-poll`设置为负值,如下例所示:

```

```
从版本 5.5 开始,
的`0`值具有特殊的含义-完全跳过`MessageSource.receive()`调用,这可能被认为是对此入站通道适配器的暂停,直到`maxMessagesPerPoll`在稍后的时间被更改为非零值,例如通过控制总线。

还请参见[全局默认 Poller](./endpoint.html#global-default-poller)以获取更多信息。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### [](#channel-adapter-namespace-outbound)配置出站通道适配器 + +一个`outbound-channel-adapter`元素(用于 Java 配置的`@ServiceActivator`)也可以将`MessageChannel`连接到任何 POJO 使用者方法,该方法应该使用发送到该通道的消息的有效负载来调用。下面的示例展示了如何定义出站通道适配器: + +Java DSL + +``` +@Bean +public IntegrationFlow outboundChannelAdapterFlow(MyPojo myPojo) { + return f -> f + .handle(myPojo, "handle"); +} +``` + +Java + +``` +public class MyPojo { + + @ServiceActivator(channel = "channel1") + void handle(Object payload) { + ... + } + +} +``` + +Kotlin DSL + +``` +@Bean +fun outboundChannelAdapterFlow(myPojo: MyPojo) = + integrationFlow { + handle(myPojo, "handle") + } +``` + +XML + +``` + + + +``` + +如果要调整的通道是`PollableChannel`,则必须提供一个 poller 子元素(`@Poller`上的`@Poller`子注释),如下例所示: + +Java + +``` +public class MyPojo { + + @ServiceActivator(channel = "channel1", poller = @Poller(fixedRate = "3000")) + void handle(Object payload) { + ... + } + +} +``` + +XML + +``` + + + + + +``` + +如果 POJO 使用者实现可以在其他``定义中重用,则应该使用`ref`属性。然而,如果消费者实现仅由``的单个定义引用,则可以将其定义为内部 Bean,如下例所示: + +``` + + + +``` + +| |不允许在同一个``配置中同时使用`ref`属性和内部处理程序定义,因为它会创建一个不明确的条件。
这样的配置会导致引发异常。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +可以在没有`channel`引用的情况下创建任何通道适配器,在这种情况下,它会隐式地创建`DirectChannel`的实例。创建的通道名称与``或``元素的`id`属性匹配。因此,如果不提供`channel`,则需要`id`。 + +#### [](#channel-adapter-expressions-and-scripts)通道适配器表达式和脚本 + +与许多其他 Spring 集成组件一样,``和``也提供了对 SPEL 表达式求值的支持。要使用 SPEL,在“expression”属性中提供表达式字符串,而不是提供用于 Bean 上的方法调用的“ref”和“method”属性。当计算一个表达式时,它遵循与方法调用相同的契约,其中:当计算结果为非空值时,``的表达式会生成消息,而``的表达式必须等同于无效返回方法调用。 + +从 Spring Integration3.0 开始,``还可以配置一个 SPEL``(甚至还可以配置一个`