@@ -42,7 +42,7 @@ Spring 鉴于所提供的 AMQP 通道适配器仅用于单向消息传递(发
小贴士:你应该熟悉[reference documentation of the Spring AMQP project](https://docs.spring.io/spring-amqp/reference/html/)。它提供了关于 Spring 与 AMQP 的集成的更深入的信息,特别是与 RabbitMQ 的集成。
在上一节中讨论的网关是同步的,因为发送线程被挂起,直到收到答复(或发生超时)。 Spring 集成版本 4.3 增加了一个异步网关,它使用 Spring AMQP 中的`AsyncRabbitTemplate`。当发送消息时,线程在发送操作完成后立即返回,当收到消息时,响应将在模板的侦听器容器线程上发送。当在 Poller 线程上调用网关时,这可能是有用的。该线程已被释放,并可用于框架中的其他任务。
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 支持的消息通道
### AMQP 支持的消息通道
有两个消息通道实现可用。一种是点对点,另一种是发布订阅。这两个通道都为底层`AmqpTemplate`和`SimpleMessageListenerContainer`提供了广泛的配置属性(如本章前面所示的通道适配器和网关)。然而,我们在这里展示的示例具有最小的配置。探索 XML 模式以查看可用的属性。
...
...
@@ -760,7 +760,7 @@ Spring AMQP 支持[RabbitMQ 延迟消息交换插件](https://docs.spring.io/spr
Spring 集成提供了许多配置选项。你选择哪一种选择取决于你的特殊需求,以及你更喜欢工作的级别。与 Spring 框架一般一样,你可以混合和匹配各种技术以适应手头的问题。例如,你可以为大多数配置选择基于 XSD 的名称空间,并将其与使用注释进行配置的几个对象结合在一起。两者尽可能地提供一致的命名。由 XSD 模式定义的 XML 元素与注释的名称匹配,并且这些 XML 元素的属性与注释属性的名称匹配。你也可以直接使用 API,但是我们希望大多数开发人员选择一个更高级别的选项,或者基于名称空间的配置和注释驱动的配置的组合。
### [](#configuration-namespace)名称空间支持
### 名称空间支持
你可以使用 XML 元素配置 Spring 集成组件,这些 XML 元素直接映射到 Enterprise 集成的术语和概念。在许多情况下,元素名与[*Enterprise 整合模式 *](https://www.enterpriseintegrationpatterns.com/)书中的元素名匹配。
...
...
@@ -70,7 +70,7 @@ Spring 集成提供了许多其他名称空间。实际上,每个提供名称
本参考手册在相应章节中提供了各种元素的具体示例。在这里,需要认识的主要问题是每个名称空间 URI 和模式位置的命名的一致性。
### [](#namespace-taskscheduler)配置任务调度程序
### 配置任务调度程序
在 Spring 集成中,`ApplicationContext`扮演着消息总线的中心角色,你只需要考虑几个配置选项。首先,你可能想要控制中心`TaskScheduler`实例。你可以通过提供一个名为`taskScheduler`的 Bean 来实现此目的。这也被定义为一个常数,如下所示:
Spring Integration4.0 之前,消息传递注释要求`inputChannel`是对`SubscribableChannel`的引用。对于`PollableChannel`实例,需要一个`<int:bridge/>`元素来配置`<int:poller/>`并使复合端点为`PollingConsumer`。版本 4.0 引入了`@Poller`注释,以允许直接在消息传递注释上配置`poller`属性,如下例所示:
...
...
@@ -319,7 +319,7 @@ public class AnnotationService {
标准的 Spring 框架`@ComponentScan`注释不扫描接口中的原型`@Component`注释。为了克服这个限制并允许`@MessagingGateway`的配置(参见[`@MessagingGateway`注释](./gateway.html#messaging-gateway-annotation)),我们引入了`@IntegrationComponentScan`机制。此注释必须与`@Configuration`注释一起放置,并进行自定义以定义其扫描选项,例如`basePackages`和`basePackageClasses`。在这种情况下,所有发现的带有`@MessagingGateway`注释的接口都被解析并注册为`GatewayProxyFactoryBean`实例。所有其他基于类的组件都由标准`@ComponentScan`解析。
为了方便起见,为了记录通过 Spring 集成流(`<logging-channel-adapter>`)的消息行程,呈现了一个`log()`操作符。在内部,它由`WireTap``ChannelInterceptor`表示,其订阅服务器为`LoggingHandler`。它负责将传入消息记录到下一个端点或当前通道中。下面的示例展示了如何使用`LoggingHandler`:
到目前为止所示的所有示例都说明了 DSL 如何通过使用 Spring 集成编程模型来支持消息传递体系结构。然而,我们还没有进行任何真正的整合。这样做需要通过 HTTP、JMS、AMQP、TCP、JDBC、FTP、SMTP 等访问远程资源,或者访问本地文件系统。 Spring 集成支持所有这些和更多内容。理想情况下,DSL 应该为所有这些功能提供一流的支持,但是实现所有这些功能并随着新的适配器被添加到 Spring 集成中而跟上,这是一项艰巨的任务。因此,期望是 DSL 不断赶上 Spring 集成。
...
...
@@ -742,7 +742,7 @@ public AbstractMappingMessageRouter xpathRouter(MessageChannel wrongMessagesChan
@@ -1017,7 +1017,7 @@ public IntegrationFlow customFlowDefinition() {
}
```
### [](#integration-flows-composition)积分流组合
### 积分流组合
在 Spring 集成中,将`MessageChannel`抽象作为第一类公民,始终假定集成流的组成。流中任何端点的输入通道都可以用于从任何其他端点发送消息,而不仅仅是从具有该通道作为输出的端点发送消息。此外,有了`@MessagingGateway`契约、内容更丰富的组件、像`<chain>`这样的复合端点,以及现在有了`IntegrationFlow`bean(例如`IntegrationFlowAdapter`),在更短的、可重用的部分之间分配业务逻辑就足够简单了。最后一篇作文所需要的只是关于`MessageChannel`发送到或接收到的信息的知识。
正如本手册开头的[overview](./overview.html#overview)中所描述的, Spring 集成之类的面向消息的框架背后的主要动机之一是促进组件之间的松耦合。消息渠道起着重要的作用,因为生产者和消费者不必相互了解。然而,这些优点也有一些缺点。在松散耦合的环境中,有些事情会变得更加复杂,其中一个例子就是错误处理。
## [](#applicationevent) Spring `ApplicationEvent`支持
## Spring `ApplicationEvent`支持
Spring 集成提供了对入站和出站`ApplicationEvents`的支持,如底层 Spring 框架所定义的那样。有关 Spring 对事件和侦听器的支持的更多信息,请参见[Spring Reference Manual](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#context-functionality-events)。
要接收事件并将其发送到通道,你可以定义 Spring Integration 的`ApplicationEventListeningMessageProducer`实例。这个类是 Spring 的`ApplicationListener`接口的实现。默认情况下,它将所有接收到的事件作为 Spring 集成消息传递。要根据事件的类型进行限制,可以使用“eventTypes”属性来配置要接收的事件类型列表。如果接收到的事件有一个`Message`实例作为其“源”,则该`Message`将按原样传递。否则,如果提供了基于 SPEL 的`payloadExpression`,则根据`ApplicationEvent`实例对其进行求值。如果事件的源不是`Message`实例,并且没有提供`payloadExpression`,则`ApplicationEvent`本身将作为有效负载传递。
...
...
@@ -79,7 +79,7 @@ public IntegrationFlow eventFlow(ApplicationEventListeningMessageProducer events
}
```
### [](#appevent-outbound)发送 Spring 应用程序事件
### 发送 Spring 应用程序事件
要发送 Spring `ApplicationEvents`,请创建`ApplicationEventPublishingMessageHandler`的一个实例,并将其注册到一个端点中。`MessageHandler`接口的此实现还实现了 Spring 的`ApplicationEventPublisherAware`接口,因此充当 Spring 集成消息和`ApplicationEvents`之间的桥梁。
对提要进行轮询可能会导致条目已经被处理(“我已经阅读了该新闻项目,为什么要再次向我显示它?”) Spring 集成提供了一种方便的机制,以消除对重复条目的担心。每个提要条目都有一个“发布日期”字段。每次生成和发送新的`Message`时, Spring 集成都会在`MetadataStore`策略的实例中存储最新发布日期的值(参见[元数据存储](./meta-data-store.html#metadata-store))。
当 Gemfire 连续查询或`CqEvent`事件触发时,连续查询入站通道适配器在通道上生成消息。在版本`1.1`中, Spring Data 引入了连续查询支持,包括`ContinuousQueryListenerContainer`,这在 Gemfire 原生 API 上提供了一个很好的抽象。这个适配器需要对`ContinuousQueryListenerContainer`实例的引用,为给定的`query`创建一个侦听器,并执行查询。连续查询充当事件源,每当其结果集更改状态时,它都会触发事件源。
| |`IntegrationRequestMappingHandlerMapping`扩展了 Spring MVC`RequestMappingHandlerMapping`类,继承了它的大部分逻辑,尤其是`handleNoMatch(Set, String, HttpServletRequest)`,当映射由于某种原因不匹配时,它会为 HTTP 响应抛出一个特定的`4xx`错误,防止调用应用程序上下文中的任何剩余映射处理程序。<br/>因此,为 Spring 集成和 Spring MVC 请求映射(例如,不支持`POST`in one 和`GET`in the other);将找不到 MVC 映射。|
从版本 4.2 开始,你可以使用`<cross-origin>`元素配置`<http:inbound-channel-adapter>`和`<http:inbound-gateway>`。它表示与 Spring MVC 的`@CrossOrigin`用于`@Controller`注释的选项相同的选项,并允许为 Spring 集成 HTTP 端点配置跨源资源共享:
@@ -340,7 +340,7 @@ Spring Integration3.0 之后,除了现有的`#pathVariables`和`#requestParams
requestAttributes.request.queryString"/>
```
#### [](#outbound)出站
#### 出站
要配置出站网关,你可以使用名称空间支持。以下代码片段显示了出站 HTTP 网关的可用配置选项:
...
...
@@ -398,7 +398,7 @@ Spring Integration3.0 之后,除了现有的`#pathVariables`和`#requestParams
| |要指定 URL,可以使用“url”属性或“url-expression”属性。<br/>“url”属性接受一个简单的字符串(对于 URI 变量有占位符,如下所述)。<br/>“url-expression”是一个 SPEL 表达式,以`Message`作为根对象,<br/>表达式求值结果的 URL 仍然可以有 URI 变量的占位符。<br/><br/>在以前的版本中,一些用户使用占位符用 URI 变量替换整个 URL,<br/> Spring 3.1 中的更改可能会导致转义字符的一些问题,例如“?”。<br/>出于这个原因,我们建议,如果你希望在运行时完全生成 URL,那么可以使用“url-expression”属性。|
@@ -32,7 +32,7 @@ Spring 尽管 JMS 通道适配器旨在用于单向消息传递(仅发送或
在 Spring Integration2.2 之前,如果有必要,将为每个请求或回复创建(并删除)一个`TemporaryQueue`。从 Spring Integration2.2 开始,你可以将出站网关配置为使用`MessageListener`容器来接收回复,而不是直接使用新的(或缓存的)`Consumer`来接收每个请求的回复。在这样配置且未提供明确的应答目的地时,每个网关使用一个`TemporaryQueue`,而不是每个请求使用一个。
`message-driven-channel-adapter`要求引用 Spring `MessageListener`容器的实例(`AbstractMessageListenerContainer`的任意子类),或者同时引用`ConnectionFactory`和`Destination`(可以提供一个“destinationName”来代替“destination”引用)。下面的示例用`Destination`引用定义了消息驱动的通道适配器:
出站网关从 Spring 集成消息创建 JMS 消息,并将它们发送到“请求-目的地”。然后,它通过使用选择器从你配置的“reply-destination”接收 JMS 回复消息,或者,如果没有提供“reply-destination”,则通过创建 JMS`TemporaryQueue`实例来处理 JMS 回复消息。
...
...
@@ -316,7 +316,7 @@ Spring Integration 的消息驱动 JMS Inbound-Gateway 委托给`MessageListener
“出站网关”有效负载提取属性与“入站网关”的有效负载提取属性是反相关的(参见[先前的讨论](#jms-message-driven-channel-adapter))。这意味着“extract-request-payload”属性值应用于正在转换为 JMS 消息的 Spring 集成消息,该消息将作为请求发送。“extract-reply-payload”属性值应用于作为答复接收的 JMS 消息,然后将其转换为 Spring 集成消息,随后将其发送到“replacy-channel”,如前面的配置示例所示。
Spring Integration2.2 引入了一种用于处理答复的替代技术。如果你在网关中添加一个`<reply-listener/>`子元素,而不是为每个回复创建一个使用者,那么将使用一个`MessageListener`容器来接收回复并将其交给请求线程。这提供了许多性能优势,并缓解了[早些时候的警告](#jms-outbound-gateway-memory-caution)中描述的缓存消费者内存利用问题。
...
...
@@ -340,13 +340,13 @@ Spring Integration2.2 引入了一种用于处理答复的替代技术。如果
JMS 消息可以包含元信息,如 JMS API 头和简单属性。你可以使用`JmsHeaderMapper`将这些消息映射到 Spring 集成消息头。JMS API 头被传递给适当的 setter 方法(例如`setJMSReplyTo`),而其他头则被复制到 JMS 消息的一般属性中。JMS 出站网关使用`JmsHeaderMapper`的默认实现来引导,它将映射标准的 JMS API 头以及原语或`String`消息头。还可以通过使用入站和出站网关的`header-mapper`属性提供自定义的头映射器。
...
...
@@ -530,7 +530,7 @@ public DefaultJmsHeaderMapper jmsHeaderMapper() {
这些 JMS 属性分别映射到`JmsHeaders.DELIVERY_MODE`和`JmsHeaders.EXPIRATION` Spring 消息头。
前面介绍的通道适配器和网关都是为与其他外部系统集成的应用程序设计的。入站选项假设其他系统正在向 JMS 目的地发送 JMS 消息,出站选项假设其他系统正在从该目的地接收消息。 Spring 集成应用程序可以是也可以不是另一种系统。当然,当发送 Spring 集成消息实例作为 JMS 消息本身的主体时(将’extract-payload’值设置为`false`),假定另一个系统是基于 Spring 集成的。然而,这绝不是一项要求。这种灵活性是使用基于消息的集成选项并抽象“通道”(在 JMS 的情况下是目标)的好处之一。
...
...
@@ -591,7 +591,7 @@ public DefaultJmsHeaderMapper jmsHeaderMapper() {
Spring 与操作调用通道适配器类似,集成还提供了一个操作调用出站网关,当需要返回值时,可以在处理非 void 操作时使用该网关。返回值作为消息有效负载发送到网关指定的`reply-channel`。下面的示例展示了如何使用 XML 配置操作调用出站网关:
...
...
@@ -138,7 +138,7 @@ Spring 与操作调用通道适配器类似,集成还提供了一个操作调
如果不提供`reply-channel`属性,则将回复消息发送到由`IntegrationMessageHeaderAccessor.REPLY_CHANNEL`标头标识的通道。该消息头通常由消息流的入口点自动创建,例如任何网关组件。但是,如果消息流是通过手动创建 Spring 集成消息并将其直接发送到通道来启动的,则必须显式地指定消息头,或者使用`reply-channel`属性。
#### [](#jmx-mbean-exporter)MBean 出口商
#### MBean 出口商
Spring 当`IntegrationMBeanExporter`被配置时,集成组件本身可以作为 MBean 公开。要创建`IntegrationMBeanExporter`的实例,定义 Bean 并提供对`MBeanServer`的引用和域名(如果需要)。可以省略这个域,在这种情况下,默认域是`org.springframework.integration`。下面的示例展示了如何声明`IntegrationMBeanExporter`实例和关联的`MBeanServer`实例:
...
...
@@ -171,7 +171,7 @@ public class ContextConfiguration {
如果需要提供更多选项或具有几个`IntegrationMBeanExporter`bean(例如用于不同的 MBean 服务器或避免与标准 Spring `MBeanExporter`冲突——例如通过`@EnableMBeanExport`),则可以将`IntegrationMBeanExporter`配置为通用 Bean。
Spring 集成还提供了对带有`MailReceivingMessageSource`的入站电子邮件的支持。它将委托给 Spring 集成自己的`MailReceiver`接口的已配置实例。有两种实现方式:`Pop3MailReceiver`和`ImapMailReceiver`。实例化这两种方法中任何一种的最简单方法是将邮件存储的“URI”传递给接收者的构造函数,如下例所示:
...
...
@@ -90,7 +90,7 @@ if (closeableResource != null) {
从版本 5.4 开始,现在可以返回`MimeMessage`,不需要任何转换或急切的内容加载。该功能是通过以下选项组合启用的:不提供`headerMapper`,`simpleContent`属性是`false`,`autoCloseFolder`属性是`false`。`MimeMessage`作为产生的 Spring 消息的有效负载而存在。在这种情况下,唯一填充的头是上面提到的`IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE`,用于在`MimeMessage`的处理完成时必须关闭的文件夹。
### [](#mail-mapping)入站邮件消息映射
### 入站邮件消息映射
默认情况下,入站适配器产生的消息的有效负载是 RAW`MimeMessage`。你可以使用该对象查询标题和内容。从版本 4.3 开始,你可以提供`HeaderMapper<MimeMessage>`来将标题映射到`MessageHeaders`。 Spring 为方便起见,集成为此目的提供了`DefaultMailHeaderMapper`。它映射了以下标题:
...
...
@@ -164,7 +164,7 @@ XML
从版本 5.4 开始,当没有`headerMapper`被提供时,`autoCloseFolder`是`false`而`simpleContent`是`false`,则在产生的 Spring 消息的有效负载中以原样返回`MimeMessage`。通过这种方式,`MimeMessage`的内容在引用时按需加载,稍后在流程中加载。上面提到的所有转换仍然有效。
### [](#mail-namespace)邮件命名空间支持
### 邮件命名空间支持
Spring 集成为与邮件相关的配置提供了一个命名空间。要使用它,请配置以下架构位置:
...
...
@@ -302,7 +302,7 @@ public interface SearchTermStrategy {
| |重要提示:IMAP Peek<br/><br/>从版本 4.1.1 开始,IMAP 邮件接收器使用`mail.imap.peek`或`mail.imaps.peek`JavaMail 属性,如果指定的话。<br/>以前,接收器忽略了该属性,并且总是设置`PEEK`标志。<br/>现在,如果你显式地将此属性设置为`false`,无论`shouldMarkMessagesRead`的设置如何,消息 ISE 都标记为`\Seen`。<br/>如果未指定,则保留先前的行为(PEEK 为`true`)。|
通常,你可能会遇到过滤收到的消息的要求(例如,你希望只读取`Subject`行中具有“ Spring 集成”的电子邮件)。你可以通过将入站邮件适配器与基于表达式的`Filter`连接起来来实现这一点。尽管它会起作用,但这种方法也有缺点。由于在通过入站邮件适配器之后将对消息进行过滤,因此所有此类消息将被标记为已读(`SEEN`)或未读(取决于`should-mark-messages-as-read`属性的值)。然而,在现实中,只有当消息通过筛选条件时,将消息标记为`SEEN`才更有用。这类似于在浏览预览窗格中的所有邮件时查看电子邮件客户端,但仅标记实际打开并读为`SEEN`的邮件。
...
...
@@ -360,7 +360,7 @@ Spring Integration2.0.4 在`inbound-channel-adapter`和`imap-idle-channel-adapte
另一个合理的问题是,在下一个轮询或空闲事件中会发生什么,或者在重新启动这样的适配器时会发生什么。是否有重复的按摩需要过滤?换句话说,如果在上一次检索中,有五条新消息,只有一条通过了过滤器,那么其他四条会发生什么情况?在下一次投票时,他们会再次通过过滤逻辑还是空闲?毕竟,它们没有被标记为`SEEN`。答案是否定的。由于电子邮件服务器设置并由 Spring 集成邮件搜索过滤器使用的另一个标志(`RECENT`),它们不会受到重复处理。文件夹实现将此标志设置为表示此邮件是此文件夹新建的。也就是说,它是从上次打开这个文件夹开始到达的。换句话说,虽然我们的适配器可能会偷看电子邮件,但它也让电子邮件服务器知道此类电子邮件已被触摸,因此电子邮件服务器应将其标记为`RECENT`。