Spring Integration 框架概述

时间:2022-12-07 10:59:02

Spring Integration 框架概述

Spring 集成提供了 Spring 编程模型的扩展,以支持众所周知的企业集成模式。 它在基于 Spring 的应用程序中实现轻量级消息传递,并支持通过声明性适配器与外部系统集成。 这些适配器提供了比 Spring 对远程处理、消息传递和调度的支持更高级别的抽象。

Spring Integration的主要目标是为构建企业集成解决方案提供一个简单的模型,同时保持关注点的分离,这对于生成可维护,可测试的代码至关重要。

弹簧集成概述

本章对 Spring 集成的核心概念和组件进行了高级介绍。 它包括一些编程技巧,以帮助您充分利用 Spring 集成。

背景

Spring 框架的一个关键主题是控制反转 (IoC)。 从最广泛的意义上讲,这意味着框架代表在其上下文中管理的组件处理职责。 组件本身被简化,因为它们被免除了这些责任。 例如,依赖关系注入减轻了组件查找或创建其依赖关系的责任。 同样,面向方面的编程通过将业务组件模块化为可重用的方面来减轻业务组件的通用横切关注点。 在每种情况下,最终结果都是一个更易于测试、理解、维护和扩展的系统。

此外,Spring 框架和产品组合为构建企业应用程序提供了全面的编程模型。 开发人员受益于此模型的一致性,尤其是它基于完善的最佳实践,例如对接口进行编程以及支持组合而不是继承。 Spring 的简化抽象和强大的支持库提高了开发人员的工作效率,同时提高了可测试性和可移植性水平。

Spring Integration的动机是这些相同的目标和原则。 它将Spring编程模型扩展到消息传递域,并建立在Spring现有的企业集成支持之上,以提供更高级别的抽象。 它支持消息驱动的体系结构,其中控制反转适用于运行时问题,例如何时应运行某些业务逻辑以及应将响应发送到何处。 它支持消息的路由和转换,以便可以集成不同的传输和不同的数据格式,而不会影响可测试性。 换句话说,消息传递和集成问题由框架处理。 业务组件与基础架构进一步隔离,开发人员从复杂的集成责任中解脱出来。

作为 Spring 编程模型的扩展,Spring 集成提供了多种配置选项,包括注释、具有命名空间支持的 XML、具有通用“bean”元素的 XML 以及底层 API 的直接使用。 该 API 基于明确定义的策略接口和非侵入式委派适配器。 Spring Integration的设计灵感来自于Spring中的常见模式与Gregor Hohpe和Bobby Woolf(Addison Wesley,2004)在Enterprise Integration Patterns中描述的众所周知的模式之间的强烈亲和力。 读过这本书的开发人员应该立即熟悉Spring Integration的概念和术语。

目标和原则

春季集成的动机是以下目标:

  • 提供用于实施复杂企业集成解决方案的简单模型。
  • 在基于 Spring 的应用程序中促进异步、消息驱动的行为。
  • 促进现有Spring用户的直观,增量采用。

Spring Integration遵循以下原则:

  • 组件应松散耦合,以实现模块化和可测试性。
  • 框架应强制分离业务逻辑和集成逻辑之间的关注点。
  • 扩展点本质上应该是抽象的(但在明确定义的边界内),以促进重用和可移植性。

主要成分

从垂直角度来看,分层架构有助于关注点分离,层之间基于接口的契约促进了松散耦合。 基于Spring的应用程序通常是这样设计的,Spring框架和产品组合为遵循企业应用程序的完整堆栈的最佳实践提供了坚实的基础。 消息驱动的体系结构增加了水平视角,但这些相同的目标仍然相关。 正如“分层体系结构”是一种非常通用和抽象的范例一样,消息传递系统通常遵循类似的抽象“管道和过滤器”模型。 “过滤器”表示能够生成或使用消息的任何组件,“管道”在过滤器之间传输消息,以便组件本身保持松散耦合。 重要的是要注意,这两种高级范式并不相互排斥。 支持“管道”的基础消息传递基础结构仍应封装在协定定义为接口的层中。 同样,“过滤器”本身应该在逻辑上位于应用程序服务层之上的层中进行管理,通过接口与这些服务进行交互,其方式与 Web 层大致相同。

消息

在 Spring 集成中,消息是任何 Java 对象的通用包装器,与框架在处理该对象时使用的元数据相结合。 它由有效负载和标头组成。 有效负载可以是任何类型的,标头包含通常需要的信息,例如 ID、时间戳、相关 ID 和返回地址。 标头还用于在连接的传输之间传递值。 例如,从收到的文件创建消息时,文件名可能存储在供下游组件访问的标头中。 同样,如果邮件的内容最终将由出站邮件适配器发送,则上游组件可以将各种属性(收件人、发件人、抄送、主题等)配置为邮件头值。 开发人员还可以在标头中存储任意键值对。

Spring Integration 框架概述

图1.消息

消息通道

消息通道表示管道和筛选器体系结构的“管道”。 生产者向通道发送消息,使用者从通道接收消息。 因此,消息通道将消息传递组件分离,并为拦截和监视消息提供了一个方便的点。

Spring Integration 框架概述

图2.消息通道

消息通道可以遵循点对点或发布-订阅语义。 使用点对点通道,最多只能有一个使用者接收发送到该通道的每条消息。 另一方面,发布-订阅通道尝试将每条消息广播给通道上的所有订阅者。 Spring Integration 支持这两种模型。

虽然“点对点”和“发布-订阅”定义了最终接收每条消息的使用者数量的两个选项,但还有另一个重要的考虑因素:通道是否应该缓冲消息? 在 Spring 集成中,可轮询通道能够缓冲队列中的消息。 缓冲的优点是它允许限制入站消息,从而防止使用者过载。 但是,顾名思义,这也增加了一些复杂性,因为如果配置了轮询器,则使用者只能从此类通道接收消息。 另一方面,连接到可订阅渠道的消费者只是消息驱动的。消息通道实现详细讨论了 Spring 集成中可用的各种通道实现。

消息终结点

Spring 集成的主要目标之一是通过控制反转来简化企业集成解决方案的开发。 这意味着您不必直接实现使用者和生产者,甚至不必在消息通道上构建消息和调用发送或接收操作。 相反,您应该能够通过基于普通对象的实现来专注于特定的域模型。 然后,通过提供声明式配置,您可以将特定于域的代码“连接”到 Spring 集成提供的消息传递基础架构。 负责这些连接的组件是消息端点。 这并不意味着必须直接连接现有应用程序代码。 任何实际的企业集成解决方案都需要一定数量的代码,这些代码侧重于集成问题,例如路由和转换。 重要的是实现集成逻辑和业务逻辑之间的关注点分离。 换句话说,与 Web 应用程序的模型-视图-控制器 (MVC) 范式一样,目标应该是提供一个薄但专用的层,将入站请求转换为服务层调用,然后将服务层返回值转换为出站回复。 下一节概述了处理这些职责的消息端点类型,在接下来的章节中,您可以看到 Spring 集成的声明式配置选项如何提供一种非侵入性的方式来使用它们中的每一个。

消息端点

消息终结点表示管道和筛选器体系结构的“筛选器”。 如前所述,终结点的主要作用是将应用程序代码连接到消息传递框架,并以非侵入性方式执行此操作。 换句话说,理想情况下,应用程序代码应该不知道消息对象或消息通道。 这类似于 MVC 范式中控制器的角色。 正如控制器处理 HTTP 请求一样,消息端点处理消息。 正如控制器映射到 URL 模式一样,消息端点也映射到消息通道。 在这两种情况下,目标是相同的:将应用程序代码与基础结构隔离开来。 这些概念和以下所有模式将在企业集成模式一书中详细讨论。 在这里,我们仅提供 Spring 集成支持的主要端点类型以及与这些类型关联的角色的高级描述。 以下各章将详细介绍并提供示例代码和配置示例。

消息转换器

消息转换器负责转换消息的内容或结构并返回修改后的消息。 可能最常见的转换器类型是将消息的有效负载从一种格式转换为另一种格式(例如从 XML 转换为)的转换器类型。 同样,转换器可以添加、删除或修改消息的标头值。​​java.lang.String​

邮件过滤器

消息过滤器确定是否应将消息传递到输出通道。 这只需要一个布尔测试方法,该方法可以检查特定的有效负载内容类型、属性值、标头是否存在或其他条件。 如果消息被接受,则会将其发送到输出通道。 如果没有,则将其删除(或者,对于更严重的实现,可以抛出)。 消息筛选器通常与发布-订阅通道结合使用,其中多个使用者可能会收到相同的消息,并使用筛选器的条件来缩小要处理的消息集的范围。​​Exception​

请注意,不要将管道和筛选器体系结构模式中“filter”的通用用法与此特定终结点类型混淆,该终结点类型选择性地缩小了两个通道之间的消息流。 “过滤器”的管道和过滤器概念与Spring Integration的消息端点更匹配:任何可以连接到消息通道以发送或接收消息的组件。

消息路由器

消息路由器负责决定接下来应接收消息的一个或多个通道(如果有)。 通常,决策基于邮件的内容或邮件头中可用的元数据。 消息路由器通常用作服务激活器或其他能够发送回复消息的端点上静态配置的输出通道的动态替代方法。 同样,消息路由器为多个订阅者使用的反应式消息过滤器提供了一种主动替代方法,如前所述。

Spring Integration 框架概述

图3.消息路由器

分配器

拆分器是另一种类型的消息端点,其职责是接受来自其输入通道的消息,将该消息拆分为多条消息,并将每条消息发送到其输出通道。 这通常用于将“复合”有效负载对象划分为包含细分有效负载的一组消息。

聚合

聚合器基本上是拆分器的镜像,是一种消息端点,它接收多条消息并将它们组合成一条消息。 事实上,聚合器通常是包含拆分器的管道中的下游使用者。 从技术上讲,聚合器比拆分器更复杂,因为它需要维护状态(要聚合的消息),决定完整的消息组何时可用,并在必要时超时。 此外,在超时的情况下,聚合器需要知道是发送部分结果、丢弃结果还是将它们发送到单独的通道。 Spring 集成提供了超时的 a、a 和可配置设置,无论 在超时时发送部分结果,并丢弃通道。​​CorrelationStrategy​​​​ReleaseStrategy​

服务激活器

服务激活器是用于将服务实例连接到消息传递系统的通用终结点。 必须配置输入消息通道,如果要调用的服务方法能够返回值,则还可以提供输出消息通道。

输出通道是可选的,因为每条消息也可能提供自己的“退货地址”标头。 此相同规则适用于所有使用方终结点。

服务激活器调用对某个服务对象的操作来处理请求消息,提取请求消息的有效负载并进行转换(如果该方法不需要消息类型的参数)。 每当服务对象的方法返回值时,该返回值也会在必要时转换为回复消息(如果它还不是消息类型)。 该回复消息将发送到输出通道。 如果未配置输出通道,则回复将发送到消息的“返回地址”中指定的通道(如果可用)。

请求-答复服务激活器终结点将目标对象的方法连接到输入和输出消息通道。

Spring Integration 框架概述

图4.服务激活器


如前所述,在消息通道中,通道可以是可轮询的,也可以是可订阅的。 在上图中,这由“时钟”符号、实线箭头(轮询)和虚线箭头(订阅)表示。

通道适配器

通道适配器是将消息通道连接到其他系统或传输的端点。 通道适配器可以是入站适配器,也可以是出站适配器。 通常,通道适配器在消息与从其他系统接收或发送到其他系统(文件、HTTP 请求、JMS 消息等)的任何对象或资源之间执行一些映射。 根据传输,通道适配器还可以填充或提取消息标头值。 Spring 集成提供了许多通道适配器,这些适配器将在后面的章节中介绍。

Spring Integration 框架概述

图5.入站通道适配器终结点将源系统连接到 。​​MessageChannel​

邮件源可以是可轮询的(例如 POP3),也可以是邮件驱动的(例如,IMAP 空闲)。 在上图中,这由“时钟”符号和实线箭头(轮询)和虚线箭头(消息驱动)描述。

Spring Integration 框架概述

图6.出站通道适配器端点将 a连接到目标系统。​​MessageChannel​

如前面的消息通道中所述,通道可以是可轮询的,也可以是可订阅的。 在上图中,这由“时钟”符号、实线箭头(轮询)和虚线箭头(订阅)表示。

端点 Bean 名称

使用终结点(任何带有 an 的终结点)由两个 bean(使用者和消息处理程序)组成。 使用者具有对消息处理程序的引用,并在消息到达时调用它。​​inputChannel​

请考虑以下 XML 示例:

<int:service-activator id = "someService" ... />

给定前面的示例,Bean 名称如下所示:

  • 消费者:(someServiceid)
  • 处理器:someService.handler

使用企业集成模式 (EIP) 注释时,名称取决于多个因素。 请考虑以下带注释的 POJO 示例:

@Component
public class SomeComponent {

@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}

}

给定前面的示例,Bean 名称如下所示:

  • 消费者:someComponent.someMethod.serviceActivator
  • 处理器:someComponent.someMethod.serviceActivator.handler

从版本 5.0.4 开始,您可以使用注释修改这些名称,如以下示例所示:​​@EndpointId​

@Component
public class SomeComponent {

@EndpointId("someService")
@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}

}

给定前面的示例,Bean 名称如下所示:

  • 消费者:someService
  • 处理器:someService.handler

创建由具有 XML 配置的属性创建的名称。 考虑以下带注释的 Bean 示例:​​@EndpointId​​​​id​

@Configuration
public class SomeConfiguration {

@Bean
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}

}

给定前面的示例,Bean 名称如下所示:

  • 消费者:someConfiguration.someHandler.serviceActivator
  • 处理程序:(名称)someHandler@Bean

从版本 5.0.4 开始,您可以使用注释修改这些名称,如以下示例所示:​​@EndpointId​

@Configuration
public class SomeConfiguration {

@Bean("someService.handler")
@EndpointId("someService")
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}

}

处理程序:(豆类名称)​​someService.handler​

使用者:(终结点 ID)​​someService​

注释创建由具有 XML 配置的属性创建的名称,只要您使用附加到名称的约定。​​@EndpointId​​​​id​​​​.handler​​​​@Bean​

有一种特殊情况是创建第三个 bean:出于体系结构原因,如果 a 未定义 an,则框架会将提供的 Bean 包装在 a 中。 此包装器支持请求处理程序建议处理,并发出正常的“未生成回复”调试日志消息。 它的 Bean 名称是处理程序 Bean 名称加号(当有一个 — 否则,它是正常生成的处理程序名称)。​​MessageHandler​​​​@Bean​​​​AbstractReplyProducingMessageHandler​​​​ReplyProducingMessageHandlerWrapper​​​​.wrapper​​​​@EndpointId​

类似地,可轮询消息源创建两个 bean,a(SPCA) 和 a。​​SourcePollingChannelAdapter​​​​MessageSource​

请考虑以下 XML 配置:

<int:inbound-channel-adapter id = "someAdapter" ... />

给定上述 XML 配置,Bean 名称如下所示:

  • 爱护动物协会:(someAdapterid)
  • 处理器:someAdapter.source

考虑以下 POJO 的 Java 配置来定义:​​@EndpointId​

@EndpointId("someAdapter")
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
public String pojoSource() {
...
}

给定前面的 Java 配置示例,Bean 名称如下所示:

  • 爱护动物协会:someAdapter
  • 处理器:someAdapter.source

考虑以下 Bean 的 Java 配置来定义:​​@EndpointID​

@Bean("someAdapter.source")
@EndpointId("someAdapter")
@InboundChannelAdapter(channel = "channel3", poller = @Poller(fixedDelay = "5000"))
public MessageSource<?> source() {
return () -> {
...
};
}

给定前面的示例,Bean 名称如下所示:

  • 爱护动物协会:someAdapter
  • 处理程序:(只要使用附加到名称的约定)someAdapter.source.source@Bean

配置和​​@EnableIntegration​

在本文档中,您可以看到对 XML 命名空间支持的引用,用于在 Spring 集成流中声明元素。 此支持由一系列命名空间解析器提供,这些解析器生成适当的 Bean 定义来实现特定组件。 例如,许多端点由 abean 和 abean 组成,处理程序和输入通道名称被注入其中。​​MessageHandler​​​​ConsumerEndpointFactoryBean​

第一次遇到 Spring Integration 命名空间元素时,框架会自动声明许多用于支持运行时环境的 bean(任务调度程序、隐式通道创建者等)。

4.0 版引入了注释,允许注册 Spring Integration 基础结构 bean(参见Javadoc​)。 当仅使用 Java 配置时,需要此注解 — 例如,使用 Spring Boot 或 Spring Integration Messaging Annotation 支持和没有 XML 集成配置的 Spring Integration Java DSL。​​@EnableIntegration​

当您有一个没有 Spring 集成组件的父上下文和两个或多个使用 Spring 集成的子上下文时,注释也很有用。 它允许在父上下文中仅声明一次这些公共组件。​​@EnableIntegration​

注释将许多基础结构组件注册到应用程序上下文中。 特别是,它:​​@EnableIntegration​

  • 注册一些内置的 bean,例如 andits,用于轮询器、SpEL 函数等。errorChannelLoggingHandlertaskSchedulerjsonPath
  • 添加多个实例以增强全局和默认集成环境。BeanFactoryPostProcessorBeanFactory
  • 添加多个实例以增强或转换和包装特定 Bean 以进行集成。BeanPostProcessor
  • 添加注释处理器以分析消息传递注释,并将这些注释的组件注册到应用程序上下文中。

注释还允许类路径扫描。 此注解的作用与标准 Spring 框架注解类似,但它仅限于特定于 Spring 集成的组件和注解,这是标准 Spring Framework 组件扫描机制无法达到的。 有关示例,请参阅@MessagingGateway批注。​​@IntegrationComponentScan​​​​@ComponentScan​

Theannotation 注册 abean 并配置 for 那些没有 aattribute 的注释。 如果找到多个注释,则它们必须具有相同的默认通道值。 有关详细信息,请参阅使用@Publisher注释的注释驱动配置。​​@EnablePublisher​​​​PublisherAnnotationBeanPostProcessor​​​​default-publisher-channel​​​​@Publisher​​​​channel​​​​@EnablePublisher​

注释已被引入到标记豆中,用于全局通道拦截。 此注释是 XML 元素的类似物(请参阅全局通道拦截器配置)。注释可以放置在类级别(使用构造型注释)或类中的 onmethod。 无论哪种情况,Bean 都必须实现。​​@GlobalChannelInterceptor​​​​ChannelInterceptor​​​​<int:channel-interceptor>​​​​@GlobalChannelInterceptor​​​​@Component​​​​@Bean​​​​@Configuration​​​​ChannelInterceptor​

从版本 5.1 开始,全局通道拦截器适用于动态注册的通道 — 例如通过使用 或通过使用 Java DSL 初始化的 bean。 以前,在刷新应用程序上下文后创建 Bean 时,不会应用拦截器。​​beanFactory.initializeBean()​​​​IntegrationFlowContext​

注释标记,,orbeans作为候选转换器。 此批注是 XML 元素的类似物(请参阅有效负载类型转换)。 您可以在类级别(使用刻板型注释)或类内的方法放置注释。​​@IntegrationConverter​​​​Converter​​​​GenericConverter​​​​ConverterFactory​​​​integrationConversionService​​​​<int:converter>​​​​@IntegrationConverter​​​​@Component​​​​@Bean​​​​@Configuration​

有关消息传递批注的详细信息,请参阅批注支持。

编程注意事项

您应该尽可能使用普通的旧 Java 对象 (POJO),并且仅在绝对必要时才在代码中公开框架。 有关详细信息,请参阅POJO 方法调用。

如果确实向类公开框架,则需要考虑一些注意事项,尤其是在应用程序启动期间:

  • 如果你的组件是,你通常不应该使用thein方法。 相反,存储引用并将此类使用推迟到上下文生命周期的后期。ApplicationContextAwareApplicationContextsetApplicationContext()
  • 如果您的组件是 anor use 方法,请不要从这些初始化方法发送任何消息。 调用这些方法时,应用程序上下文尚未初始化,发送此类消息可能会失败。 如果需要在启动期间发送消息,请实现并等待。 或者,实现,将 Bean 置于后期阶段,然后从方法发送消息。InitializingBean@PostConstructApplicationListenerContextRefreshedEventSmartLifecyclestart()

使用包装(例如,阴影)罐子时的注意事项

Spring 集成通过使用 Spring Framework 的机制来加载多个类来引导某些功能。 这包括罐子以及某些其他罐子,包括和。 此过程的信息存储在每个 jar 的文件中。​​SpringFactories​​​​IntegrationConfigurationInitializer​​​​-core​​​​-http​​​​-jmx​​​​META-INF/spring.factories​

一些开发人员更喜欢使用众所周知的工具(例如Apache Maven Shade 插件)将他们的应用程序和所有依赖项重新打包到一个 jar 中。

默认情况下,阴影插件在生成着色 jar 时不会合并文件。​​spring.factories​

除此之外,其他文件(和)还用于 XML 配置。 这些文件也需要合并。​​spring.factories​​​​META-INF​​​​spring.handlers​​​​spring.schemas​

Spring Boot 的可执行 jar 机制​采用了不同的方法,因为它嵌套了 jar,从而将每个文件保留在类路径上。 因此,对于 Spring Boot 应用程序,如果您使用其默认的可执行 jar 格式,则不需要更多内容。​​spring.factories​

即使您不使用 Spring Boot,您仍然可以使用 Boot 提供的工具,通过为上述文件添加转换器来增强 shade 插件。 以下示例显示了如何配置插件:

例 1.绒球.xml

...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>true</createDependencyReducedPom>
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
...

具体说来

添加依赖项。​​spring-boot-maven-plugin​

配置转换器。

您可以添加属性或使用显式版本。​​${spring.boot.version}​

编程提示和技巧

本节记录了一些充分利用 Spring 集成的方法。

XML 架构

使用 XML 配置时,为了避免出现错误的模式验证错误,您应该使用“Spring-aware”IDE,例如 Spring Tool Suite (STS)、带有 Spring IDE 插件的 Eclipse 或 IntelliJ IDEA。 这些 IDE 知道如何从类路径解析正确的 XML 架构(通过使用 jar 中的文件)。 将 STS 或 Eclipse 与插件一起使用时,必须启用项目。​​META-INF/spring.schemas​​​​Spring Project Nature​

出于兼容性原因,互联网上托管的某些遗留模块(版本 1.0 中存在的模块)的架构是 1.0 版本。 如果 IDE 使用这些架构,则可能会看到错误。

其中每个联机架构都有一个类似于以下内容的警告:


此架构适用于 Spring 集成核心的 1.0 版本。 我们无法将其更新为当前架构,因为这会破坏使用 1.0.3 或更低版本的任何应用程序。 对于后续版本,“未版本化”模式从类路径解析并从 jar 获取。 请参考 GitHub:



https://github.com/spring-projects/spring-integration/tree/main/spring-integration-core/src/main/resources/org/springframework/integration/config


受影响的模块是

  • ​core​​ (spring-integration.xsd)
  • ​file​
  • ​http​
  • ​jms​
  • ​mail​
  • ​security​
  • ​stream​
  • ​ws​
  • ​xml​

查找 Java 和 DSL 配置的类名

通过 XML 配置和 Spring Integration Namespace 支持,XML 解析器隐藏了目标 bean 的声明和连接方式。 对于 Java 配置,了解目标最终用户应用程序的框架 API 非常重要。

EIP 实施的一等公民是 和(请参阅本章前面的主要组件)。 它们的实现(合同)是:​​Message​​​​Channel​​​​Endpoint​

  • ​org.springframework.messaging.Message​​:请参阅消息;
  • ​org.springframework.messaging.MessageChannel​​:请参阅消息通道;
  • ​org.springframework.integration.endpoint.AbstractEndpoint​​:请参阅投票器。

前两个非常简单,可以理解如何实现、配置和使用。 最后一个值得更多关注

Theis在整个Spring框架中广泛用于不同的组件实现。 其主要实现方式是:​​AbstractEndpoint​

  • ​EventDrivenConsumer​​,当我们订阅 ato 监听消息时使用。SubscribableChannel
  • ​PollingConsumer​​,当我们轮询来自 A 的消息时使用。PollableChannel

当您使用消息传递注释或 Java DSL 时,您无需担心这些组件,因为框架会使用适当的注释和实现自动生成它们。 手动构建组件时,应使用 to 帮助确定要创建的目标使用者实现,基于提供的属性。​​BeanPostProcessor​​​​ConsumerEndpointFactoryBean​​​​AbstractEndpoint​​​​inputChannel​

另一方面,委托给框架中的另一个一等公民 - 。 此接口实现的目标是处理终结点从通道使用的消息。 Spring 集成中的所有 EIP 组件都是实现(例如,,,,和其他)。 目标协议出站适配器(,,, 和其他适配器)也是实现。 当您使用 Java 配置开发 Spring 集成应用程序时,您应该查看 Spring 集成模块以找到用于配置的合适实现。 例如,要发送 XMPP 消息(请参阅XMPP 支持),您应该配置如下内容:​​ConsumerEndpointFactoryBean​​​​org.springframework.messaging.MessageHandler​​​​MessageHandler​​​​AggregatingMessageHandler​​​​MessageTransformingHandler​​​​AbstractMessageSplitter​​​​FileWritingMessageHandler​​​​HttpRequestExecutingMessageHandler​​​​AbstractMqttMessageHandler​​​​MessageHandler​​​​MessageHandler​​​​@ServiceActivator​

@Bean
@ServiceActivator(inputChannel = "input")
public MessageHandler sendChatMessageHandler(XMPPConnection xmppConnection) {
ChatMessageSendingMessageHandler handler = new ChatMessageSendingMessageHandler(xmppConnection);

DefaultXmppHeaderMapper xmppHeaderMapper = new DefaultXmppHeaderMapper();
xmppHeaderMapper.setRequestHeaderNames("*");
handler.setHeaderMapper(xmppHeaderMapper);

return handler;
}

实现表示消息流的出站和处理部分。​​MessageHandler​

入站消息流端有自己的组件,这些组件分为轮询行为和侦听行为。 侦听(消息驱动)组件很简单,通常只需要一个目标类实现即可准备就绪 生成消息。 侦听组件可以是单向实现(如 and)或请求-回复实现(如 and)。​​MessageProducerSupport​​​​AbstractMqttMessageDrivenChannelAdapter​​​​ImapIdleChannelAdapter​​​​MessagingGatewaySupport​​​​AmqpInboundGateway​​​​AbstractWebServiceInboundGateway​

轮询入站终结点适用于不提供侦听器 API 或不用于 此类行为,包括任何基于文件的协议(如 FTP)、任何数据库(RDBMS 或 NoSQL)等。

这些入站终端节点由两个组件组成:轮询器配置,用于定期启动轮询任务, 以及一个消息源类,用于从目标协议读取数据并为下游集成流生成消息。 轮询器配置的第一个类是 a。 这是另一种实现,但特别是对于轮询以启动集成流。 通常,使用消息传递注解或 Java DSL,您不必担心此类。 框架根据配置或Java DSL构建器规范为它生成一个bean。​​SourcePollingChannelAdapter​​​​AbstractEndpoint​​​​@InboundChannelAdapter​

消息源组件对于目标应用程序开发更为重要,它们都实现了接口(例如,和)。 考虑到这一点,我们使用 JDBC 从 RDBMS 表中读取数据的配置可能类似于以下内容:​​MessageSource​​​​MongoDbMessageSource​​​​AbstractTwitterMessageSource​

@Bean
@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixedDelay="5000"))
public MessageSource<?> storedProc(DataSource dataSource) {
return new JdbcPollingChannelAdapter(dataSource, "SELECT * FROM foo where status = 0");
}

您可以在特定的 Spring 集成模块中找到目标协议所需的所有入站和出站类(在大多数情况下,在相应的包中)。 例如,适配器是:​​spring-integration-websocket​

  • ​o.s.i.websocket.inbound.WebSocketInboundChannelAdapter​​:实现侦听套接字上的帧并向通道生成消息。MessageProducerSupport
  • ​o.s.i.websocket.outbound.WebSocketOutboundMessageHandler​​:将传入消息转换为相应帧并通过 websocket 发送的单向实现。AbstractMessageHandler

如果您熟悉 Spring Integration XML 配置,从版本 4.3 开始,我们将在 XSD 元素定义,说明哪些目标类用于为适配器或网关声明 Bean,如以下示例所示:

<xsd:element name="outbound-async-gateway">
<xsd:annotation>
<xsd:documentation>
Configures a Consumer Endpoint for the 'o.s.i.amqp.outbound.AsyncAmqpOutboundGateway'
that will publish an AMQP Message to the provided Exchange and expect a reply Message.
The sending thread returns immediately; the reply is sent asynchronously; uses 'AsyncRabbitTemplate.sendAndReceive()'.
</xsd:documentation>
</xsd:annotation>

POJO 方法调用

如编程注意事项中所述,我们建议使用 POJO 编程风格,如以下示例所示:

@ServiceActivator
public String myService(String payload) { ... }

在这种情况下,框架提取有效负载,调用您的方法,并将结果包装在消息中,以发送到流中的下一个组件(原始标头将复制到新消息中)。 事实上,如果使用 XML 配置,则甚至不需要注释,如以下配对示例所示:​​String​​​​@ServiceActivator​

<int:service-activator ... ref="myPojo" method="myService" />
public String myService(String payload) { ... }

只要类上的公共方法中没有歧义,就可以省略该属性。​​method​

您还可以在 POJO 方法中获取标头信息,如以下示例所示:

@ServiceActivator
public String myService(@Payload String payload, @Header("foo") String fooHeader) { ... }

还可以取消引用消息的属性,如以下示例所示:

@ServiceActivator
public String myService(@Payload("payload.foo") String foo, @Header("bar.baz") String barbaz) { ... }

由于可以使用各种 POJO 方法调用,因此 5.0 之前的版本使用 SpEL(Spring 表达式语言)来调用 POJO 方法。 与方法中通常完成的实际工作相比,SpEL(甚至解释)对于这些操作通常“足够快”。 但是,从版本 5.0 开始,默认情况下尽可能使用。 这种技术通常比解释型 SpEL 执行得更快,并且与其他 Spring 消息传递项目一致。 这类似于Spring MVC中用于调用控制器方法的技术。 使用 SpEL 时,仍会始终调用某些方法。 示例包括具有取消引用属性的批注参数,如前所述。 这是因为 SpEL 具有导航属性路径的功能。​​org.springframework.messaging.handler.invocation.InvocableHandlerMethod​​​​InvocableHandlerMethod​

可能还有其他一些我们没有考虑过的极端情况,这些情况也不适用于实例。 出于这个原因,在这些情况下,我们会自动回退到 SpEL 的使用。​​InvocableHandlerMethod​

如果您愿意,您还可以设置 POJO 方法,使其始终使用 SpEL 和注释,如以下示例所示:​​UseSpelInvoker​

@UseSpelInvoker(compilerMode = "IMMEDIATE")
public void bar(String bar) { ... }

如果省略该属性,则系统属性确定编译器模式。 有关已编译的 SpEL 的更多信息,请参阅SpEL 编译。​​compilerMode​​​​spring.expression.compiler.mode​