配置
配置
Spring 集成提供了许多配置选项。 您选择哪个选项取决于您的特定需求以及您喜欢在哪个级别工作。 与一般的 Spring 框架一样,您可以混合和匹配各种技术以适应手头的问题。 例如,您可以为大多数配置选择基于 XSD 的命名空间,并将其与使用注释配置的少数对象组合在一起。 这两者尽可能提供一致的命名。 XSD 架构定义的 XML 元素与注释的名称匹配,并且这些 XML 元素的属性与注释属性的名称匹配。 您也可以直接使用 API,但我们希望大多数开发人员选择更高级别的选项之一,或者选择基于命名空间和注释驱动的配置的组合。
命名空间支持
你可以使用 XML 元素配置 Spring 集成组件,这些元素直接映射到企业集成的术语和概念。 在许多情况下,元素名称与 Enterprise Integration Patterns 书籍中的名称相匹配。
要在 Spring 配置文件中启用 Spring 集成的核心名称空间支持,请在顶级 'beans' 元素中添加以下名称空间引用和架构映射:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
(我们已经强调了 Spring Integration 特有的行.)
您可以选择 “xmlns:” 之后的任何名称。
我们使用int
(Integration 的缩写),但您可能更喜欢其他缩写。
另一方面,如果您使用 XML 编辑器或 IDE 支持,则自动完成的可用性可能会说服您保留较长的名称以清楚起见。
或者,你可以创建使用 Spring 集成模式作为主命名空间的配置文件,如下例所示:
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">
(我们已经强调了 Spring Integration 特有的行.)
当使用这种替代方法时,Spring 集成元素不需要前缀。
另一方面,如果在同一配置文件中定义一个通用的 Spring Bean,则 bean 元素需要一个前缀 (<beans:bean …/>
).
由于模块化配置文件本身(基于职责或体系结构层)通常是一个好主意,因此您可能会发现在以集成为中心的配置文件中使用后一种方法是合适的,因为在这些文件中很少需要通用 bean。
在本文档中,我们假设集成命名空间是主要命名空间。
Spring 集成提供了许多其他命名空间。
事实上,提供名称空间支持的每个适配器类型(JMS、文件等)都在单独的架构中定义其元素。
为了使用这些元素,请使用xmlns
条目和相应的schemaLocation
映射。
例如,以下根元素显示了其中的几个命名空间声明:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-file="http://www.springframework.org/schema/integration/file"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
http://www.springframework.org/schema/integration/jms
https://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd
http://www.springframework.org/schema/integration/mail
https://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
http://www.springframework.org/schema/integration/ws
https://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
...
</beans>
本参考手册在其相应章节中提供了各种元素的具体示例。 在这里,需要识别的主要内容是每个命名空间 URI 和 schema 位置的命名的一致性。
配置 Task Scheduler
在 Spring 集成中,ApplicationContext
在消息总线中起着核心作用,您只需考虑几个配置选项。
首先,您可能希望控制中央TaskScheduler
实例。
为此,您可以提供一个名为taskScheduler
.
这也被定义为一个常量,如下所示:
IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME
默认情况下, Spring 集成依赖于ThreadPoolTaskScheduler
,如 Spring Framework 参考手册的 Task Execution and Scheduling 部分所述。
该默认值TaskScheduler
使用包含 10 个线程的池自动启动,但请参阅 全局属性。
如果您提供自己的TaskScheduler
实例,则可以将 'autoStartup' 属性设置为false
或提供您自己的池大小值。
当轮询使用者在其配置中提供显式任务执行程序引用时,处理程序方法的调用发生在该执行程序的线程池中,而不是主计划程序池中。 但是,当没有为端点的 Poller 提供任务执行程序时,它由主调度程序的线程之一调用。
不要在 Poller 线程上运行长时间运行的任务。
请改用任务执行程序。
如果有很多轮询终端节点,则可能会导致线程匮乏,除非增加池大小。
此外,轮询使用者有一个默认的receiveTimeout 的 1 秒。
由于 poller 线程此时会阻塞,因此我们建议您在存在许多此类端点时使用 task executor,以避免匮乏。
或者,您也可以减少receiveTimeout . |
如果终端节点的 input 通道是基于队列的 (即 pollable) 通道之一,则该终端节点是 Polling Consumer 。 事件驱动型使用者是指那些具有调度程序而不是队列的输入通道的使用者(换句话说,它们是可订阅的)。 这样的端点没有 Poller 配置,因为它们的处理程序是直接调用的。 |
当自定义TaskScheduler 在应用程序上下文中配置(如上所述DefaultManagedTaskScheduler ),建议为其提供MessagePublishingErrorHandler (integrationMessagePublishingErrorHandler bean) 以便能够将异常作为ErrorMessage`s sent to the error channel, as is done with the default `TaskScheduler Bean 的 Bean 提供。 |
有关更多信息,另请参阅 错误处理 。
全局属性
可以通过在 Classpath 上提供 properties 文件来覆盖某些全局框架属性。
默认属性可以在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 | 如果为 true,则input-channel 实例自动声明为DirectChannel 实例。 |
2 | 设置允许的默认订阅者数量,例如DirectChannel .
它可用于避免无意中将多个终端节点订阅到同一通道。
您可以通过将max-subscribers 属性。 |
3 | 此属性提供允许在PublishSubscribeChannel .
它可用于避免无意中为同一通道订阅超过预期数量的终端节点。
您可以通过将max-subscribers 属性。 |
4 | 默认值中可用的线程数taskScheduler 豆。
请参阅配置 Task Scheduler。 |
5 | 什么时候true 时,当网关不期待回复时(因为发送线程已超时或已经收到回复),到达网关回复通道的消息将引发异常。 |
6 | 不应填充到的邮件报头名称的逗号分隔列表Message 实例。
该列表由DefaultMessageBuilderFactory bean 并传播到IntegrationMessageHeaderAccessor 实例(请参阅MessageHeaderAccessor 应用程序接口) 用于通过MessageBuilder (参见这MessageBuilder Helper 类).
默认情况下,只有MessageHeaders.ID 和MessageHeaders.TIMESTAMP 在消息构建期间不会复制。
从 4.3.2 版本开始。 |
7 | 以逗号分隔的AbstractEndpoint Bean 名称模式 (xxx* ,xxx ,*xxx 或xxx*yyy ),该 ID 不应在应用程序启动期间自动启动。
您可以稍后通过Control Bus (请参阅 Control Bus),根据它们在SmartLifecycleRoleController (请参阅 Endpoint Roles ),或通过Lifecycle bean 注入。
您可以通过指定auto-startup XML 注解或autoStartup annotation 属性或通过调用AbstractEndpoint.setAutoStartup() 在 bean 定义中。
从 4.3.12 版本开始。 |
8 | 一个布尔标志,用于指示默认全局errorChannel 必须配置requireSubscribers 选择。
从 5.4.3 版本开始。
有关更多信息,请参阅错误处理。 |
9 | 一个布尔标志,用于指示默认全局errorChannel 必须忽略 dispatch 错误并将消息传递给下一个处理程序。
从 5.5 版本开始。 |
可以通过添加/META-INF/spring.integration.properties
file 添加到类路径中,或者将IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME
bean 的org.springframework.integration.context.IntegrationProperties
实例。
您无需提供所有属性,只需提供要覆盖的属性即可。
从版本 5.1 开始,当DEBUG
逻辑电平为org.springframework.integration
类别。
输出如下所示:
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
注释支持
除了用于配置消息端点的 XML 名称空间支持之外,您还可以使用注释。
首先,Spring Integration 提供了类级@MessageEndpoint
作为构造型注释,这意味着它本身使用 Spring 的@Component
注解,因此被 Spring 的组件扫描自动识别为 bean 定义。
更重要的是各种方法级 Comments。 它们指示带注释的方法能够处理消息。 以下示例演示了类级和方法级 Comments:
@MessageEndpoint
public class FooService {
@ServiceActivator
public void processMessage(Message message) {
...
}
}
“处理”Message 的方法的确切含义取决于特定的 Comments。 Spring Integration 中可用的 Comments 包括:
-
@Aggregator
(请参阅 聚合器) -
@Filter
(请参阅过滤器) -
@Router
(请参阅 路由器) -
@ServiceActivator
(请参阅 Service Activator) -
@Splitter
(请参阅 Splitter) -
@Transformer
(请参阅 Transformer) -
@InboundChannelAdapter
(请参阅 Channel Adapter) -
@BridgeFrom
(请参阅使用 Java 配置配置 Bridge) -
@BridgeTo
(请参阅使用 Java 配置配置 Bridge) -
@MessagingGateway
(请参阅消息网关) -
@IntegrationComponentScan
(参见Configuration 和@EnableIntegration
)
如果您将 XML 配置与注解结合使用,则@MessageEndpoint 注释不是必需的。
如果要从ref 属性<service-activator/> 元素中,您只能提供方法级注解。
在这种情况下,注解可以防止歧义,即使<service-activator/> 元素。 |
在大多数情况下,带注解的处理程序方法不应要求Message
type 作为其参数。
相反,method 参数类型可以与消息的有效负载类型匹配,如下例所示:
public class ThingService {
@ServiceActivator
public void bar(Thing thing) {
...
}
}
当 method 参数应从MessageHeaders
,另一个选项是使用 parameter-level@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<String, Object> headerMap) {
...
}
}
注释的值也可以是 SPEL 表达式(例如,someHeader.toUpperCase() ),当您希望在注入 header 值之前对其进行作时,这非常有用。
它还提供了一个可选的required 属性,该属性指定属性值是否必须在标头中可用。
的required property 为true . |
对于其中几个注释,当消息处理方法返回非 null 值时,终端节点会尝试发送回复。
这在两个配置选项(namespace 和 annotations)中是一致的,因为使用了这种端点的输出通道(如果可用),并且REPLY_CHANNEL
message header 值用作回退。
端点上的输出通道和回复通道消息头的组合支持管道方法,其中多个组件有一个输出通道,最终组件允许将回复消息转发到回复通道(如原始请求消息中指定)。 换句话说,最终组件取决于原始发送方提供的信息,并且可以动态支持任意数量的客户端。 这是返回地址模式的一个示例。 |
除了此处显示的示例之外,这些注解还支持inputChannel
和outputChannel
属性,如下例所示:
@Service
public class ThingService {
@ServiceActivator(inputChannel="input", outputChannel="output")
public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
...
}
}
这些 Comments 的处理将创建与相应的 XML 组件相同的 bean——AbstractEndpoint
instances 和MessageHandler
实例(或MessageSource
实例)。
看注释@Bean
方法.
Bean 名称由以下模式生成:[componentName].[methodName].[decapitalizedAnnotationClassShortName]
.
在前面的示例中,Bean 名称为thingService.otherThing.serviceActivator
对于AbstractEndpoint
和相同的名称,并附加一个.handler
(.source
) 后缀的MessageHandler
(MessageSource
) bean 的 Bean 中。
这样的名称可以使用@EndpointId
注释以及这些消息注释。
这MessageHandler
实例 (MessageSource
实例)也有资格被消息历史记录跟踪。
从版本 4.0 开始,所有消息收发注释都提供SmartLifecycle
选项 (autoStartup
和phase
) 以允许对应用程序上下文初始化进行终端节点生命周期控制。
它们默认为true
和0
分别。
要更改终端节点的状态(例如start()
或stop()
),您可以使用BeanFactory
(或自动装配)并调用方法。
或者,您也可以将命令消息发送到Control Bus
(请参阅 Control Bus)。
为此,您应该使用beanName
前面在上一段中提到过。
在解析上述注释后自动创建的通道(当没有配置特定的通道 bean 时)和相应的消费者端点,在上下文初始化结束时被声明为 bean。
这些 bean 可以在其他服务中自动连接,但它们必须用
|
从版本 6.0 开始,所有消息传递注释都是@Repeatable
现在,可以在同一个 service method 上声明几个相同类型的 Token,其含义是创建与这些 Comments 重复的 Endpoints 一样多的 Endpoints:
@Transformer(inputChannel = "inputChannel1", outputChannel = "outputChannel1")
@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel2")
public String transform(String input) {
return input.toUpperCase();
}
使用@Poller
注解
在 Spring Integration 4.0 之前,消息传递注释要求inputChannel
是对SubscribableChannel
.
为PollableChannel
实例、<int:bridge/>
元素来配置<int:poller/>
并将复合端点设为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
annotation 仅提供简单的PollerMetadata
选项。
您可以配置@Poller
注解的属性 (maxMessagesPerPoll
,fixedDelay
,fixedRate
和cron
) 替换为属性占位符。
此外,从版本 5.1 开始,receiveTimeout
选项PollingConsumer
s 的
如果需要提供更多轮询选项(例如,transaction
,advice-chain
,error-handler
等),您应该配置PollerMetadata
作为通用 Bean 使用,并使用其 Bean 名称作为@Poller
的value
属性。
在这种情况下,不允许使用其他属性(必须在PollerMetadata
bean) 的 Bean 的
注意,如果inputChannel
是一个PollableChannel
没有@Poller
,则默认的PollerMetadata
(如果它存在于应用程序上下文中)。
要使用@Configuration
annotation 中,请使用类似于以下示例的代码:
@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
annotation 具有errorChannel
属性,以便更轻松地配置底层MessagePublishingErrorHandler
.
此属性的作用与error-channel
在<poller>
XML 组件。
有关更多信息,请参阅终端节点命名空间支持。
这poller()
属性与reactive()
属性。
有关更多信息,请参阅下一节。
用@Reactive
注解
这ReactiveStreamsConsumer
自 5.0 版本以来一直存在,但仅当端点的输入通道为FluxMessageChannel
(或任何org.reactivestreams.Publisher
实现)。
从版本 5.3 开始,当目标消息处理程序是ReactiveMessageHandler
独立于 input channel type。
这@Reactive
sub-annotation (类似于上面提到的@Poller
) 已针对从版本 5.5 开始的所有消息传递注释引入。
它接受一个可选的Function<? super Flux<Message<?>>, ? extends Publisher<Message<?>>>
Bean 引用,并且独立于输入通道类型和消息处理程序,将目标端点转换为ReactiveStreamsConsumer
实例。
该函数从Flux.transform()
运算符应用一些自定义 (publishOn()
,doOnNext()
,log()
,retry()
等)在来自 input 通道的反应式流源上。
下面的示例演示了如何将发布线程从独立于最终订阅者和生产者的 input 通道更改为该DirectChannel
:
@Bean
public Function<Flux<?>, Flux<?>> publishOnCustomizer() {
return flux -> flux.publishOn(Schedulers.parallel());
}
@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer"))
public void handleReactive(String payload) {
...
}
这reactive()
属性与poller()
属性。
看使用@Poller
注解和 Reactive Streams Support 了解更多信息。
使用@InboundChannelAdapter
注解
版本 4.0 引入了@InboundChannelAdapter
方法级注解。
它生成一个SourcePollingChannelAdapter
集成组件基于MethodInvokingMessageSource
对于带注释的方法。
此注释类似于<int:inbound-channel-adapter>
XML 组件,并且具有相同的限制:方法不能有参数,并且返回类型不能是void
.
它有两个属性:value
(必需的MessageChannel
Bean 名称)和poller
(可选的@Poller
注释,如前所述)。
如果您需要提供一些MessageHeaders
,请使用Message<?>
return 类型并使用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
annotation 属性,以提供更好的源代码可读性。
此外,目标MessageChannel
bean 在SourcePollingChannelAdapter
通过提供的名称(由outputChannelName
选项)在第一个receive()
调用,而不是在初始化阶段。
它允许 “late binding” 逻辑:目标MessageChannel
从消费者的角度来看,bean 的创建和注册时间比@InboundChannelAdapter
parsing 阶段。
第一个示例要求已在应用程序上下文中的其他位置声明默认 Poller。
使用@MessagingGateway
注解
使用@IntegrationComponentScan
注解
标准的 Spring 框架@ComponentScan
annotation 不扫描接口中的构造型@Component
附注。
为了克服此限制并允许配置@MessagingGateway
(参见@MessagingGateway
注解),我们引入了@IntegrationComponentScan
机制。
此注释必须与@Configuration
注释并自定义以定义其扫描选项,
如basePackages
和basePackageClasses
.
在这种情况下,所有发现的带有@MessagingGateway
被解析并注册为GatewayProxyFactoryBean
实例。
所有其他基于 class 的组件都由标准@ComponentScan
.
消息元注释
从版本 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) {
...
}
通过分层配置元注释,用户可以为各种属性设置默认值,并支持将框架 Java 依赖项隔离到用户注释,从而避免在用户类中使用它们。 如果框架找到具有具有框架元注释的用户注释的方法,则会将其视为该方法直接使用框架注释进行注释。
注释@Bean
方法
从版本 4.0 开始,您可以在@Bean
方法定义@Configuration
类来生成基于 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<String> consoleSource() {
return CharacterStreamReadingMessageSource.stdin();
}
@Bean
@Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel")
public ObjectToMapTransformer toMapTransformer() {
return new ObjectToMapTransformer();
}
@Bean
@ServiceActivator(inputChannel = "httpChannel")
public HttpRequestExecutingMessageHandler 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<String> pojoSupplier() {
return () -> "foo";
}
@Bean
@InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000"))
public Supplier<Message<String>> messageSupplier() {
return () -> new GenericMessage<>("foo");
}
}
元注释规则适用于@Bean
方法(@MyServiceActivator
注解可以应用于@Bean
定义)。
当您在 consumer 上使用这些注解时@Bean 定义,如果 Bean 定义返回适当的MessageHandler (取决于注解类型),您必须设置属性(例如outputChannel ,requiresReply ,order 等)、在MessageHandler @Bean 定义本身。
仅使用以下注释属性:adviceChain ,autoStartup ,inputChannel ,phase 和poller .
所有其他属性都用于处理程序。 |
Bean 名称是使用以下算法生成的: |
-
这
MessageHandler
(MessageSource
)@Bean
从方法名称中获取自己的标准名称,或者name
属性@Bean
. 这就像@Bean
方法。 -
这
AbstractEndpoint
bean 名称使用以下模式生成:[@Bean name].[decapitalizedAnnotationClassShortName]
. 例如,SourcePollingChannelAdapter
endpoint 的consoleSource()
前面显示的定义得到一个 bean 名称consoleSource.inboundChannelAdapter
. 与 POJO 方法不同,端点 Bean 名称中不包含 Bean 方法名称。 另请参见端点 Bean 名称。 -
如果
@Bean
不能直接在目标终端节点中使用(不是MessageSource
,AbstractReplyProducingMessageHandler
或AbstractMessageRouter
)、相应的AbstractStandardMessageHandlerFactoryBean
已注册以委托给此@Bean
. 此包装器的 bean 名称是使用以下模式生成的:[@Bean name].[decapitalizedAnnotationClassShortName].[handler (or source)]
.
在@Bean 定义、inputChannel 必须引用已声明的 Bean。
如果应用程序上下文中尚不存在 channels,则会自动声明 channel。 |
通过 Java 配置,您可以使用任何
与现有的 Spring 容器逻辑一起,消息传递端点 Bean(基于 |
创建带有注释的桥接
从版本 4.0 开始,Java 配置提供了@BridgeFrom
和@BridgeTo
@Bean
要标记的方法注释MessageChannel
豆子@Configuration
类。
这些确实是为了完整性而存在的,提供了一种方便的机制来声明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();
}
您也可以将这些注释用作元注释。
建议带注释的终端节点
请参阅使用注释通知终端节点。
消息映射规则和约定
Spring 集成通过依赖一些默认规则和定义某些约定,实现了一种灵活的工具,可以将消息映射到方法及其参数,而无需提供额外的配置。 以下各节中的示例阐明了这些规则。
示例场景
以下示例显示了一个未注释的参数(对象或基元),该参数不是Map
或Properties
Object 的 object:
public String doSomething(Object o);
input 参数是消息负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值将合并为返回消息的有效负载。
以下示例显示了一个未注释的参数(对象或基元),该参数不是Map
或Properties
替换为Message
返回类型:
public Message doSomething(Object o);
input 参数是消息负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值是发送到下一个目标的新构造的消息。
下面的示例显示了一个参数,该参数是具有任意对象或原始返回类型的消息(或其子类之一):
public int doSomething(Message msg);
输入参数本身是一个Message
.
返回值将成为Message
,该 ID 将被发送到下一个目的地。
以下示例显示了一个参数,该参数是Message
(或其子类之一)替换为Message
(或其子类之一)作为返回类型:
public Message doSomething(Message msg);
输入参数本身是一个Message
.
返回值是新构造的Message
,该 ID 将被发送到下一个目的地。
以下示例显示了Map
或Properties
替换为Message
作为返回类型:
public Message doSomething(Map m);
这个有点有趣。
尽管乍一看,直接映射到消息标头似乎很容易,但始终优先考虑Message
有效载荷。
这意味着,如果Message
payload 的类型为Map
,则此 input 参数表示Message
有效载荷。
但是,如果Message
payload 不是类型Map
,则转换服务不会尝试转换有效负载,并且 input 参数将映射到消息标头。
以下示例显示了两个参数,其中一个参数是任意类型(对象或基元),而不是Map
或Properties
object 的 object 类型为Map
或Properties
type (无论返回值如何):
public Message doSomething(Map h, <T> t);
此组合包含两个输入参数,其中一个是Map
.
非Map
参数(无论顺序如何)映射到Message
payload 和Map
或Properties
(无论顺序如何)映射到消息标头,为您提供一种很好的 POJO 交互方式Message
结构。
以下示例显示无参数(无论返回结果如何):
public String doSomething();
此消息处理程序方法是根据发送到此处理程序所连接的输入通道的 Message 调用的。
然而,没有Message
data 被映射,从而使Message
充当事件或触发器来调用处理程序。
根据前面描述的规则映射输出。
以下示例显示无参数和 void 返回:
public void soSomething();
此示例与上一个示例相同,但它不会生成任何输出。
基于注释的映射
基于 Comments 的映射是将消息映射到方法的最安全且最不明确的方法。 下面的示例演示如何将方法显式映射到标头:
public String doSomething(@Payload String s, @Header("someheader") String b)
正如您稍后所看到的,如果没有 Annotation,此签名将导致模棱两可的条件。
但是,通过将第一个参数显式映射到Message
payload 和第二个参数设置为someheader
message 标头,我们避免了任何歧义。
以下示例与前面的示例几乎相同:
public String doSomething(@Payload String s, @RequestParam("something") String b)
@RequestMapping
或任何其他非 Spring 集成映射注释都是无关紧要的,因此被忽略,使第二个参数未映射。
尽管第二个参数可以很容易地映射到有效负载,但只能有一个有效负载。
因此,注释使此方法不明确。
以下示例显示了另一种类似的方法,如果不是 annotation 来阐明意图,该方法将是模棱两可的:
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' 和 'someotherthing' 的消息标头的值。
有效负载未映射到任何参数。
复杂场景
以下示例使用多个参数:
多个参数可能会在确定适当的映射时产生很多歧义。
一般的建议是用@Payload
,@Header
和@Headers
.
本节中的示例显示了导致引发异常的不明确条件。
public String doSomething(String s, int i)
这两个参数的权重相等。 因此,无法确定哪一个是有效负载。
以下示例显示了一个类似的问题,只有三个参数:
public String foo(String s, Map m, String b)
尽管 Map 可以很容易地映射到消息头,但无法确定如何处理这两个 String 参数。
以下示例显示了另一种不明确的方法:
public String foo(Map m, Map f)
尽管有人可能会争辩说Map
可以映射到消息 payload,另一个映射到消息头,我们不能依赖 order。
具有多个方法参数且不是 (Map ,<T> ) 和未注释的参数会导致不明确的条件并触发异常。 |
下一组示例分别显示了导致歧义的多种方法。
具有多个方法的消息处理程序根据前面描述的相同规则进行映射(在示例中)。 但是,某些方案可能仍然看起来令人困惑。
以下示例显示了具有合法(可映射和明确)签名的多种方法:
public class Something {
public String doSomething(String str, Map m);
public String doSomething(Map m);
}
(方法具有相同的名称或不同的名称没有区别)。
这Message
可以映射到任一方法。
当消息有效负载可以映射到str
并且消息标头可以映射到m
.
第二种方法也可以是候选方法,只需将消息标头映射到m
.
更糟糕的是,这两种方法具有相同的名称。
起初,由于以下配置,这可能看起来模棱两可:
<int:service-activator input-channel="input" output-channel="output" method="doSomething">
<bean class="org.things.Something"/>
</int:service-activator>
它之所以有效,是因为 Map 首先基于有效负载,然后基于其他所有内容。 换句话说,其第一个参数可以映射到有效负载的方法优先于所有其他方法。
现在考虑另一个示例,它会产生一个真正模棱两可的条件:
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
属性来指示映射:
<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse">
<bean class="org.bar.Foo"/>
</int:service-activator>
由于配置显式地将doSomethingElse
方法,我们已经消除了歧义。