概述
Spring 集成概述
本章对 Spring 集成的核心概念和组件进行了高级介绍。 它包括一些编程技巧,以帮助您充分利用 Spring Integration。
背景
Spring 框架的关键主题之一是控制反转 (IoC)。 从最广泛的意义上讲,这意味着框架代表在其上下文中管理的组件处理职责。 组件本身被简化,因为它们被免除了这些责任。 例如,依赖项注入减轻了组件查找或创建其依赖项的责任。 同样,面向方面的编程通过将业务组件模块化为可重用的方面来减轻它们的一般横切关注点。 在每种情况下,最终结果都是一个更易于测试、理解、维护和扩展的系统。
此外,Spring 框架和产品组合为构建企业应用程序提供了一个全面的编程模型。 开发人员受益于此模型的一致性,尤其是它基于完善的最佳实践这一事实,例如对接口进行编程以及支持组合而不是继承。 Spring 的简化抽象和强大的支持库提高了开发人员的工作效率,同时提高了可测试性和可移植性级别。
Spring Integration 的动机是这些相同的目标和原则。 它将 Spring 编程模型扩展到消息传递域中,并构建在 Spring 现有的企业集成支持之上,以提供更高级别的抽象。 它支持消息驱动架构,其中控制反转适用于运行时关注点,例如何时应运行某些业务逻辑以及应将响应发送到何处。 它支持消息的路由和转换,以便可以集成不同的传输和不同的数据格式,而不会影响可测试性。 换句话说,消息传递和集成问题由框架处理。 业务组件与基础架构进一步隔离,开发人员可以减轻复杂的集成责任。
作为 Spring 编程模型的扩展,Spring 集成提供了多种配置选项,包括注释、具有名称空间支持的 XML、具有通用“bean”元素的 XML 以及底层 API 的直接使用。 该 API 基于定义明确的策略接口和非侵入性委托适配器。 Spring 集成的设计灵感来自于对 Spring 中的常见模式与 Gregor Hohpe 和 Bobby Woolf (Addison Wesley, 2004) 在 Enterprise Integration Patterns 中描述的众所周知的模式之间的强烈亲和力的认识。 读过这本书的开发人员应该立即熟悉 Spring 集成的概念和术语。
目标和原则
Spring Integration 的动机是以下目标:
-
为实施复杂的企业集成解决方案提供一个简单的模型。
-
在基于 Spring 的应用程序中促进异步、消息驱动的行为。
-
促进现有 Spring 用户直观、渐进地采用。
Spring 集成遵循以下原则:
-
组件应该是松散耦合的,以实现模块化和可测试性。
-
框架应该强制在业务逻辑和集成逻辑之间分离关注点。
-
扩展点本质上应该是抽象的(但在明确定义的边界内),以促进重用和可移植性。
主要组件
从垂直角度来看,分层架构有助于关注点分离,而层之间基于接口的契约促进了松散耦合。 基于 Spring 的应用程序通常以这种方式设计,Spring 框架和产品组合为遵循企业应用程序的整个堆栈的最佳实践提供了坚实的基础。 消息驱动型架构增加了一个水平视角,但这些相同的目标仍然相关。 正如 “分层架构” 是一种极其通用和抽象的范式一样,消息传递系统通常遵循类似的抽象 “管道和过滤器” 模型。 “过滤器”表示能够生成或使用消息的任何组件,而“管道”在过滤器之间传输消息,以便组件本身保持松散耦合。 需要注意的是,这两个高级范例并不是互斥的。 支持 “管道” 的底层消息传递基础结构仍应封装在其 Contract 定义为接口的层中。 同样,“过滤器”本身应该在逻辑上位于应用程序服务层之上的层中进行管理,通过接口与这些服务进行交互的方式与 Web 层大致相同。
消息
在 Spring 集成中,消息是任何 Java 对象的通用包装器,它与框架在处理该对象时使用的元数据相结合。 它由 payload 和 headers 组成。 有效负载可以是任何类型,标头包含通常需要的信息,例如 ID、时间戳、相关 ID 和返回地址。 标头还用于向连接的传输传递值或从连接的传输传递值。 例如,从收到的文件创建消息时,文件名可以存储在 Headers 中,以供下游组件访问。 同样,如果消息的内容最终将由出站邮件适配器发送,则各种属性(to、from、cc、subject 和其他)可以由上游组件配置为消息头值。 开发人员还可以在标头中存储任意键值对。

消息通道
消息通道表示管道和过滤器架构的 “管道”。 创建者向通道发送消息,使用者从通道接收消息。 因此,消息通道将消息传递组件解耦,并为消息的拦截和监视提供了一个方便的点。

消息通道可以遵循点对点或发布-订阅语义。 使用点对点通道时,不超过一个使用者可以接收发送到该通道的每条消息。 另一方面,发布-订阅通道尝试将每条消息广播给通道上的所有订阅者。 Spring 集成支持这两种模型。
虽然 “point-to-point” 和 “publish-subscribe” 定义了最终接收每条消息的消费者数量的两个选项,但还有另一个重要的考虑因素:通道是否应该缓冲消息? 在 Spring 集成中,可轮询通道能够缓冲队列中的消息。 缓冲的优点是它允许限制入站消息,从而防止使用者过载。 然而,顾名思义,这也增加了一些复杂性,因为只有在配置了 Poller 的情况下,消费者才能从这样的通道接收消息。 另一方面,连接到可订阅通道的使用者只是消息驱动的。消息通道实现 详细讨论了 Spring 集成中可用的各种通道实现。
消息端点
Spring Integration 的主要目标之一是通过控制反转来简化企业集成解决方案的开发。 这意味着您不必直接实现使用者和创建者,甚至不必在消息通道上构建消息并调用 send 或 receive作。 相反,您应该能够使用基于普通对象的实现来专注于您的特定域模型。 然后,通过提供声明式配置,你可以将特定于域的代码“连接”到 Spring 集成提供的消息传递基础结构。 负责这些连接的组件是消息终端节点。 这并不意味着您必须直接连接现有的应用程序代码。 任何实际的企业集成解决方案都需要一些代码,这些代码专注于集成问题,例如路由和转换。 重要的是实现集成逻辑和业务逻辑之间的关注点分离。 换句话说,与 Web 应用程序的模型-视图-控制器 (MVC) 范例一样,目标应该是提供一个精简但专用的层,将入站请求转换为服务层调用,然后将服务层返回值转换为出站回复。 下一节概述了处理这些职责的消息端点类型,在接下来的章节中,你可以看到 Spring Integration 的声明式配置选项如何提供一种非侵入性的方式来使用它们中的每一个。
消息端点
消息端点表示管道和过滤器架构的 “过滤器”。 如前所述,端点的主要作用是将应用程序代码连接到消息传递框架,并以非侵入性的方式执行此作。 换句话说,理想情况下,应用程序代码应该不知道消息对象或消息通道。 这类似于 MVC 范例中控制器的角色。 就像控制器处理 HTTP 请求一样,消息端点处理消息。 正如控制器映射到 URL 模式一样,消息端点也映射到消息通道。 在这两种情况下,目标都是相同的:将应用程序代码与基础架构隔离开来。 这些概念和所有后续模式在 Enterprise Integration Patterns 一书中进行了详细讨论。 在这里,我们只提供了 Spring Integration 支持的主要端点类型以及与这些类型关联的角色的高级描述。 以下各章详细阐述并提供了示例代码和配置示例。
消息转换器
消息转换器负责转换消息的内容或结构并返回修改后的消息。
最常见的转换器类型可能是将消息的有效负载从一种格式转换为另一种格式(例如从 XML 转换为java.lang.String
).
同样,转换器可以添加、删除或修改消息的 Headers 值。
消息过滤器
消息过滤器确定是否应将消息传递到输出通道。
这只需要一个布尔测试方法,该方法可以检查特定的有效负载内容类型、属性值、标头的存在或其他条件。
如果消息被接受,则会将其发送到 output channel。
否则,它会被丢弃(或者,对于更严重的实现,一个Exception
可以抛出)。
消息过滤器通常与发布-订阅通道结合使用,其中多个使用者可能会收到相同的消息,并使用过滤器的条件来缩小要处理的消息集的范围。
请注意,不要将管道和过滤器架构模式中 “filter” 的通用用法与这种特定的端点类型混淆,后者选择性地缩小两个通道之间的消息流。 “过滤器”的管道和过滤器概念与 Spring 集成的消息端点更紧密地匹配:任何可以连接到消息通道以发送或接收消息的组件。 |
消息路由器
消息路由器负责决定接下来应该接收消息的一个或多个通道(如果有)。 通常,该决定基于消息的内容或消息标头中可用的元数据。 消息路由器通常用作服务激活器或其他能够发送回复消息的端点上静态配置的输出通道的动态替代方案。 同样,如前所述,消息路由器为多个订阅者使用的反应式消息过滤器提供了一种主动替代方案。

分配器
拆分器是另一种类型的消息端点,其职责是接受来自其 Importing 通道的消息,将该消息拆分为多个消息,并将每条消息发送到其输出通道。 这通常用于将 “复合” 有效负载对象划分为一组包含细分有效负载的消息。
聚合
聚合器基本上是拆分器的镜像,是一种消息端点,它接收多条消息并将它们组合成一条消息。
事实上,聚合器通常是包含拆分器的管道中的下游使用者。
从技术上讲,聚合器比拆分器更复杂,因为它需要维护状态(要聚合的消息),决定整个消息组何时可用,并在必要时超时。
此外,在超时的情况下,聚合器需要知道是发送部分结果、丢弃它们还是将它们发送到单独的通道。
Spring 集成提供了一个CorrelationStrategy
一个ReleaseStrategy
以及超时的可配置设置、是否
在 timeout 时发送 partial 结果,以及 discard 通道。
服务激活器
Service Activator 是用于将服务实例连接到消息传递系统的通用端点。 必须配置 input message channel,并且,如果要调用的服务方法能够返回一个值,则还可以提供输出消息 Channel。
output channel 是可选的,因为每条消息也可能提供自己的 'Return Address' 标头。 此规则适用于所有使用方终端节点。 |
服务激活器调用对某个服务对象的作来处理请求消息,提取请求消息的有效负载并进行转换(如果该方法不需要消息类型的参数)。 每当服务对象的方法返回一个值时,该返回值也会在必要时转换为回复消息(如果它还不是消息类型)。 该回复消息将发送到 output channel。 如果未配置输出通道,则回复将发送到消息的“返回地址”中指定的通道(如果可用)。
请求-回复服务激活器终端节点将目标对象的方法连接到输入和输出消息通道。

如前所述,在 Message Channel 中,通道可以是可轮询的,也可以是可订阅的。 在上图中,这由 “clock” 符号和实线箭头 (poll) 和虚线箭头 (subscribe) 表示。 |
Channel Adapter
通道适配器是将消息通道连接到其他系统或传输的端点。 通道适配器可以是入站的,也可以是出站的。 通常,通道适配器在消息与从其他系统接收或发送到其他系统的任何对象或资源(文件、HTTP 请求、JMS 消息等)之间执行一些映射。 根据传输方式,通道适配器还可以填充或提取消息头值。 Spring 集成提供了许多通道适配器,这些适配器将在接下来的章节中描述。

MessageChannel
.消息源可以是可轮询的(例如,POP3)或消息驱动的(例如,IMAP Idle)。 在上图中,这由“时钟”符号和实线箭头 (poll) 和虚线箭头 (message-driven) 表示。 |

MessageChannel
添加到目标系统。正如前面在 Message Channel 中讨论的那样,通道可以是可轮询的或可订阅的。 在上图中,这由 “clock” 符号和实线箭头 (poll) 和虚线箭头 (subscribe) 表示。 |
端点 Bean 名称
使用终端节点(任何带有inputChannel
) 由两个 bean 组成,即 consumer 和 message handler。
使用者具有对消息处理程序的引用,并在消息到达时调用它。
请考虑以下 XML 示例:
<int:service-activator id = "someService" ... />
给定前面的示例,bean 名称如下:
-
消费者:
someService
(id
) -
处理器:
someService.handler
使用 Enterprise Integration Pattern (EIP) 注释时,名称取决于多个因素。 考虑以下带 Comments 的 POJO 示例:
@Component
public class SomeComponent {
@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}
}
给定前面的示例,bean 名称如下:
-
消费者:
someComponent.someMethod.serviceActivator
-
处理器:
someComponent.someMethod.serviceActivator.handler
从版本 5.0.4 开始,您可以使用@EndpointId
annotation 中,如下例所示:
@Component
public class SomeComponent {
@EndpointId("someService")
@ServiceActivator(inputChannel = ...)
public String someMethod(...) {
...
}
}
给定前面的示例,bean 名称如下:
-
消费者:
someService
-
处理器:
someService.handler
这@EndpointId
创建由id
属性。
考虑以下带 Comments 的 bean 示例:
@Configuration
public class SomeConfiguration {
@Bean
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}
}
给定前面的示例,bean 名称如下:
-
消费者:
someConfiguration.someHandler.serviceActivator
-
处理器:
someHandler
(@Bean
名称)
从版本 5.0.4 开始,您可以使用@EndpointId
annotation 中,如下例所示:
@Configuration
public class SomeConfiguration {
@Bean("someService.handler") (1)
@EndpointId("someService") (2)
@ServiceActivator(inputChannel = ...)
public MessageHandler someHandler() {
...
}
}
1 | 处理器:someService.handler (Bean 名称) |
2 | 消费者:someService (终端节点 ID) |
这@EndpointId
annotation 创建的名称由id
属性替换为 XML 配置,只要使用附加.handler
到@Bean
名字。
在一种特殊情况下,会创建第三个 Bean:出于架构原因,如果MessageHandler
@Bean
未定义AbstractReplyProducingMessageHandler
,框架会将提供的 bean 包装在ReplyProducingMessageHandlerWrapper
.
此包装器支持请求处理程序建议处理,并发出正常的“生成无回复”调试日志消息。
它的 bean 名称是处理程序 bean 名称加上.wrapper
(当存在@EndpointId
— 否则,它是正常生成的处理程序名称)。
同样,Pollable Message Sources 创建两个 bean,一个SourcePollingChannelAdapter
(SPCA) 和MessageSource
.
请考虑以下 XML 配置:
<int:inbound-channel-adapter id = "someAdapter" ... />
给定前面的 XML 配置,bean 名称如下:
-
爱护动物协会:
someAdapter
(id
) -
处理器:
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
名称)
Configuration 和@EnableIntegration
在本文档中,你可以看到对 XML 名称空间支持的引用,以便在 Spring 集成流中声明元素。
这种支持由一系列名称空间解析器提供,这些解析器生成适当的 bean 定义来实现特定组件。
例如,许多终端节点由MessageHandler
bean 和ConsumerEndpointFactoryBean
处理程序和 input 通道名称被注入其中。
第一次遇到 Spring 集成名称空间元素时,框架会自动声明一些用于支持运行时环境的 bean(任务调度程序、隐式通道创建者等)。
版本 4.0 引入了@EnableIntegration 注解,以允许注册 Spring 集成基础设施 bean(参见 Javadoc)。
当仅使用 Java 配置时,需要此注释——例如,使用 Spring Boot 或 Spring 集成消息传递注释支持以及没有 XML 集成配置的 Spring 集成 Java DSL。 |
这@EnableIntegration
当你有一个没有 Spring 集成组件的父上下文,并且有两个或多个使用 Spring 集成的子上下文时,注释也很有用。
它允许这些公共组件在父上下文中仅声明一次。
这@EnableIntegration
annotation 将许多基础结构组件注册到 Application 上下文。
特别是,它:
-
注册一些内置 bean,例如
errorChannel
及其LoggingHandler
,taskScheduler
对于轮询者,jsonPath
SpEL 函数等。 -
添加多个
BeanFactoryPostProcessor
实例来增强BeanFactory
适用于全局和默认集成环境。 -
添加多个
BeanPostProcessor
实例来增强或转换和包装特定 bean 以进行集成。 -
添加注释处理器以解析消息传递注释,并在应用程序上下文中为它们注册组件。
这@IntegrationComponentScan
Annotation 还允许 Classpath 扫描。
这个 Comments 起着与标准 Spring Framework 类似的角色@ComponentScan
注解,但它仅限于特定于 Spring 集成的组件和注解,标准 Spring Framework 组件扫描机制无法触及这些组件和注解。
有关示例,请参阅@MessagingGateway
注解.
这@EnablePublisher
annotation 注册一个PublisherAnnotationBeanPostProcessor
bean 并配置default-publisher-channel
对于那些@Publisher
注解,则没有channel
属性。
如果有多个@EnablePublisher
annotation 时,它们必须具有相同的 default 通道值。
看注解驱动的配置,使用@Publisher
注解了解更多信息。
这@GlobalChannelInterceptor
注解已引入 MarkChannelInterceptor
bean 进行全局通道拦截。
此注释类似于<int:channel-interceptor>
XML 元素(请参见Global Channel Interceptor Configuration)。@GlobalChannelInterceptor
注解可以放置在类级别(使用@Component
stereotype 注解)或@Bean
方法中的@Configuration
类。
在任何一种情况下,bean 都必须实现ChannelInterceptor
.
从版本 5.1 开始,全局通道拦截器适用于动态注册的通道——例如使用beanFactory.initializeBean()
或通过IntegrationFlowContext
使用 Java DSL 时。
以前,在刷新应用程序上下文后创建 bean 时,不会应用拦截器。
这@IntegrationConverter
注释标记Converter
,GenericConverter
或ConverterFactory
bean 作为候选转换器integrationConversionService
.
此注释类似于<int:converter>
XML 元素(请参阅 负载类型转换)。
您可以将@IntegrationConverter
类级别的注解(带有@Component
stereotype 注解)或@Bean
方法中的@Configuration
类。
有关消息收发注释的更多信息,请参阅注释支持。
编程注意事项
您应该尽可能使用普通的旧 java 对象 (POJO),并且仅在绝对必要时在代码中公开框架。 有关更多信息,请参阅 POJO 方法调用。
如果您确实将框架公开给类,则需要考虑一些注意事项,尤其是在应用程序启动期间:
-
如果您的组件是
ApplicationContextAware
,通常不应使用ApplicationContext
在setApplicationContext()
方法。 相反,请存储引用并将此类使用推迟到上下文生命周期的后期。 -
如果您的组件是
InitializingBean
或使用@PostConstruct
方法,请不要从这些初始化方法发送任何消息。 调用这些方法时,应用程序上下文尚未初始化,并且发送此类消息可能会失败。 如果您需要在启动过程中发送消息,请实现ApplicationListener
并等待ContextRefreshedEvent
. 或者,实现SmartLifecycle
,将 bean 置于后期阶段,并从start()
方法。
使用打包的(例如,阴影)Jar 时的注意事项
Spring 集成通过使用 Spring Framework 的SpringFactories
加载多个IntegrationConfigurationInitializer
类。
这包括-core
jar 以及某些其他 jar 以及某些其他 jar 的-http
和-jmx
.
此过程的信息存储在META-INF/spring.factories
文件。
一些开发人员喜欢使用众所周知的工具(如 Apache Maven Shade Plugin)将其应用程序和所有依赖项重新打包到单个 jar 中。
默认情况下,shade 插件不会合并spring.factories
文件。
除了spring.factories
其他META-INF
文件 (spring.handlers
和spring.schemas
) 用于 XML 配置。
这些文件也需要合并。
Spring Boot 的可执行 jar 机制采用了不同的方法,因为它嵌套了 jar,从而保留了每个spring.factories file 添加到类路径上。
因此,对于 Spring Boot 应用程序,如果您使用其默认的可执行 jar 格式,则不需要更多内容。 |
即使你不使用 Spring Boot,你仍然可以使用 Boot 提供的工具,通过为上述文件添加转换器来增强 shade 插件。 以下示例显示了如何配置插件:
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>true</createDependencyReducedPom>
</configuration>
<dependencies>
<dependency> (1)
<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> (2)
<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>
...
具体说来
1 | 添加spring-boot-maven-plugin 作为依赖项。 |
2 | 配置转换器。 |
您可以为${spring.boot.version}
或使用显式版本。
编程提示和技巧
本节记录了一些充分利用 Spring 集成的方法。
XML 架构
使用 XML 配置时,为避免出现错误的架构验证错误,您应该使用“Spring 感知”IDE,例如 Spring Tool Suite (STS)、带有 Spring IDE 插件的 Eclipse 或 IntelliJ IDEA。
这些 IDE 知道如何从类路径中解析正确的 XML 架构(通过使用META-INF/spring.schemas
文件)。
将 STS 或 Eclipse 与插件一起使用时,您必须启用Spring Project Nature
在项目中。
出于兼容性原因,某些旧版模块(版本 1.0 中存在的模块)在 Internet 上托管的架构是 1.0 版本。 如果您的 IDE 使用这些架构,则可能会看到错误。
这些在线架构中的每一个都有类似于以下内容的警告:
此架构适用于 Spring Integration Core 的 1.0 版本。 我们无法将其更新为当前架构,因为这会破坏任何使用 1.0.3 或更低版本的应用程序。 对于后续版本,“unversioned” 模式从 Classpath 解析并从 jar 中获取。 请参考 GitHub: |
受影响的模块包括
-
core
(spring-integration.xsd
) -
file
-
http
-
jms
-
mail
-
security
-
stream
-
ws
-
xml
查找 Java 和 DSL 配置的类名
通过 XML 配置和 Spring 集成名称空间支持,XML 解析器隐藏了目标 bean 的声明和连接方式。 对于 Java 配置,了解目标最终用户应用程序的 Framework API 非常重要。
EIP 实施的一等公民是Message
,Channel
和Endpoint
(请参阅本章前面的 Main Components)。
他们的实现 (合约) 是:
前两个非常简单,可以理解如何实现、配置和使用。 最后一个值得更多关注
这AbstractEndpoint
在整个 Spring Framework 中广泛用于不同的组件实现。
它的主要实现是:
-
EventDrivenConsumer
,当我们订阅SubscribableChannel
以侦听消息。 -
PollingConsumer
,当我们轮询来自PollableChannel
.
当您使用消息传递注释或 Java DSL 时,您无需担心这些组件,因为 Framework 会自动使用适当的注释和BeanPostProcessor
实现。
手动构建组件时,您应该使用ConsumerEndpointFactoryBean
帮助确定目标AbstractEndpoint
consumer 实现来创建,根据提供的inputChannel
财产。
另一方面,ConsumerEndpointFactoryBean
委托给框架中的另一个一等公民 -org.springframework.messaging.MessageHandler
.
此接口的实现目标是处理端点从通道使用的消息。
Spring 集成中的所有 EIP 组件都是MessageHandler
实现(例如AggregatingMessageHandler
,MessageTransformingHandler
,AbstractMessageSplitter
等)。
目标协议出站适配器 (FileWritingMessageHandler
,HttpRequestExecutingMessageHandler
,AbstractMqttMessageHandler
等)也是MessageHandler
实现。
当您使用 Java 配置开发 Spring 集成应用程序时,您应该查看 Spring 集成模块以找到合适的MessageHandler
实现以用于@ServiceActivator
配置。
例如,要发送 XMPP 消息(请参阅 XMPP 支持),您应该配置如下内容:
@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
implementations 表示消息流的 outbound 和 processing 部分。
入站消息流端有自己的组件,这些组件分为轮询和侦听行为。
侦听 (消息驱动) 组件很简单,通常只需要一个目标类实现即可准备好
生成消息。
侦听组件可以是单向的MessageProducerSupport
实现中,(例如AbstractMqttMessageDrivenChannelAdapter
和ImapIdleChannelAdapter
) 或 request-replyMessagingGatewaySupport
实现(例如AmqpInboundGateway
和AbstractWebServiceInboundGateway
).
轮询入站终端节点适用于不提供侦听器 API 或不用于 此类行为,包括任何基于文件的协议(如 FTP)、任何数据库(RDBMS 或 NoSQL)等。
这些入站端点由两个组件组成:poller 配置,用于定期启动轮询任务,
以及一个 Message Source 类,用于从目标协议读取数据并为下游集成流生成消息。
poller 配置的第一个类是SourcePollingChannelAdapter
.
这是另一个AbstractEndpoint
实施,尤其是用于启动集成流的轮询。
通常,对于消息传递注释或 Java DSL,您不必担心此类。
框架根据@InboundChannelAdapter
配置或 Java DSL 构建器规范。
消息源组件对于目标应用程序开发来说更为重要,它们都实现了MessageSource
接口(例如MongoDbMessageSource
和AbstractTwitterMessageSource
).
考虑到这一点,我们使用 JDBC 从 RDBMS 表中读取数据的配置可能类似于以下内容:
@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
监听 socket 上的帧并向 Channel 生成消息。 -
o.s.i.websocket.outbound.WebSocketOutboundMessageHandler
:单向AbstractMessageHandler
实现将传入消息转换为适当的框架并通过 WebSocket 发送。
如果您熟悉 Spring 集成 XML 配置,则从版本 4.3 开始,我们在 关于使用哪些目标类来声明适配器或网关的 bean 的 XSD 元素定义,如下例所示:
<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) { ... }
在这种情况下,框架会提取一个String
payload 调用您的方法,并将结果包装在消息中以发送到流中的下一个组件(原始标头将复制到新消息中)。
事实上,如果您使用 XML 配置,您甚至不需要@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 开始,org.springframework.messaging.handler.invocation.InvocableHandlerMethod
默认尽可能使用。
这种技术通常比解释型 SPEL 执行得更快,并且与其他 Spring 消息传递项目一致。
这InvocableHandlerMethod
类似于在 Spring MVC 中用于调用控制器方法的技术。
在使用 SPEL 时,仍会始终调用某些方法。
示例包括具有取消引用属性的带注释的参数,如前所述。
这是因为 SPEL 具有导航属性路径的能力。
可能还有一些我们没有考虑过的其他极端情况也不适用于InvocableHandlerMethod
实例。
因此,在这些情况下,我们会自动回退到使用 SpEL。
如果你愿意,你还可以设置你的 POJO 方法,使其始终使用 SPEL,使用UseSpelInvoker
annotation 中,如下例所示:
@UseSpelInvoker(compilerMode = "IMMEDIATE")
public void bar(String bar) { ... }
如果compilerMode
属性,则spring.expression.compiler.mode
system 属性确定编译器模式。
有关已编译的 SPEL 的更多信息,请参阅 SPEL 编译。