配置

配置

Spring 集成提供了许多配置选项。 您选择哪个选项取决于您的特定需求以及您喜欢在哪个级别工作。 与一般的 Spring 框架一样,您可以混合和匹配各种技术以适应手头的问题。 例如,您可以为大多数配置选择基于 XSD 的命名空间,并将其与使用注释配置的少数对象组合在一起。 这两者尽可能提供一致的命名。 XSD 架构定义的 XML 元素与注释的名称匹配,并且这些 XML 元素的属性与注释属性的名称匹配。 您也可以直接使用 API,但我们希望大多数开发人员选择更高级别的选项之一,或者选择基于命名空间和注释驱动的配置的组合。spring-doc.cn

命名空间支持

你可以使用 XML 元素配置 Spring 集成组件,这些元素直接映射到企业集成的术语和概念。 在许多情况下,元素名称与 Enterprise Integration Patterns 书籍中的名称相匹配。spring-doc.cn

要在 Spring 配置文件中启用 Spring 集成的核心名称空间支持,请在顶级 'beans' 元素中添加以下名称空间引用和架构映射:spring-doc.cn

<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 特有的行.)spring-doc.cn

您可以选择 “xmlns:” 之后的任何名称。 为清楚起见,我们使用(Integration 的缩写),但您可能更喜欢其他缩写。 另一方面,如果您使用 XML 编辑器或 IDE 支持,则自动完成的可用性可能会说服您保留较长的名称以清楚起见。 或者,你可以创建使用 Spring 集成模式作为主命名空间的配置文件,如下例所示:intspring-doc.cn

<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-doc.cn

当使用这种替代方法时,Spring 集成元素不需要前缀。 另一方面,如果在同一配置文件中定义通用 Spring Bean,则 bean 元素需要前缀 ()。 由于模块化配置文件本身(基于职责或体系结构层)通常是一个好主意,因此您可能会发现在以集成为中心的配置文件中使用后一种方法是合适的,因为在这些文件中很少需要通用 bean。 在本文档中,我们假设集成命名空间是主要命名空间。<beans:bean …​/>spring-doc.cn

Spring 集成提供了许多其他命名空间。 事实上,提供名称空间支持的每个适配器类型(JMS、文件等)都在单独的架构中定义其元素。 要使用这些元素,请使用条目和相应的映射添加必要的命名空间。 例如,以下根元素显示了其中的几个命名空间声明:xmlnsschemaLocationspring-doc.cn

<?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 位置的命名的一致性。spring-doc.cn

配置 Task Scheduler

在 Spring 集成中,它起着消息总线的核心作用,你只需要考虑几个配置选项。 首先,您可能希望控制中央实例。 您可以通过提供一个名为 . 这也被定义为一个常量,如下所示:ApplicationContextTaskSchedulertaskSchedulerspring-doc.cn

IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME

默认情况下, Spring 集成依赖于 的实例,如 Spring Framework 参考手册的 Task Execution and Scheduling 部分所述。 该默认值会自动使用包含 10 个线程的池启动,但请参阅 Global Properties。 如果您提供自己的实例,则可以将 'autoStartup' 属性设置为或提供您自己的池大小值。ThreadPoolTaskSchedulerTaskSchedulerTaskSchedulerfalsespring-doc.cn

当轮询使用者在其配置中提供显式任务执行程序引用时,处理程序方法的调用发生在该执行程序的线程池中,而不是主计划程序池中。 但是,当没有为端点的 Poller 提供任务执行程序时,它由主调度程序的线程之一调用。spring-doc.cn

不要在 Poller 线程上运行长时间运行的任务。 请改用任务执行程序。 如果有很多轮询终端节点,则可能会导致线程匮乏,除非增加池大小。 此外,轮询使用者的默认值为 1 秒。 由于 poller 线程此时会阻塞,因此我们建议您在存在许多此类端点时使用 task executor,以避免匮乏。 或者,您也可以减少 .receiveTimeoutreceiveTimeout
如果终端节点的 input 通道是基于队列的 (即 pollable) 通道之一,则该终端节点是 Polling Consumer 。 事件驱动型使用者是指那些具有调度程序而不是队列的输入通道的使用者(换句话说,它们是可订阅的)。 这样的端点没有 Poller 配置,因为它们的处理程序是直接调用的。

在 JEE 容器中运行时,您可能需要使用 Spring 的 ,如此所述,而不是默认的 . 为此,请为您的环境定义一个具有适当 JNDI 名称的 Bean,如下例所示:TimerManagerTaskSchedulertaskSchedulerspring-doc.cn

<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler">
    <property name="jndiName" value="tm/MyTimerManager" />
    <property name="resourceRef" value="true" />
</bean>
在应用程序上下文中配置自定义时(如上文所述),建议为其提供 ( bean) ,以便能够作为框架提供的 bean 处理异常。TaskSchedulerDefaultManagedTaskSchedulerMessagePublishingErrorHandlerintegrationMessagePublishingErrorHandlerErrorMessage`s sent to the error channel, as is done with the default `TaskScheduler

有关更多信息,另请参阅 错误处理spring-doc.cn

全局属性

可以通过在 Classpath 上提供 properties 文件来覆盖某些全局框架属性。spring-doc.cn

默认属性可以在 class 中找到。 下面的清单显示了默认值:org.springframework.integration.context.IntegrationPropertiesspring-doc.cn

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-channelDirectChannel
2 设置允许在 . 它可用于避免无意中将多个终端节点订阅到同一通道。 您可以通过设置属性在单个通道上覆盖它。DirectChannelmax-subscribers
3 此属性提供允许在 . 它可用于避免无意中为同一通道订阅超过预期数量的终端节点。 您可以通过设置属性在单个通道上覆盖它。PublishSubscribeChannelmax-subscribers
4 默认 Bean 中可用的线程数。 请参阅配置 Task SchedulertaskScheduler
5 When ,到达网关回复通道的消息在网关不期待回复时(因为发送线程已超时或已收到回复)时引发异常。true
6 在报头复制操作期间不应填充到实例中的消息报头名称的逗号分隔列表。 该列表由 Bean 使用,并传播到用于构建消息的实例(参见MessageHeaderAccessor API)(参见MessageBuilder Helper Class)。 默认情况下,在消息构建期间,仅复制 和 不复制。 从 4.3.2 版本开始。MessageDefaultMessageBuilderFactoryIntegrationMessageHeaderAccessorMessageBuilderMessageHeaders.IDMessageHeaders.TIMESTAMP
7 在应用程序启动期间不应自动启动的 bean 名称模式(、、*xxx 或 )的逗号分隔列表。 您可以稍后通过它们的 bean 名称(参见 Control Bus)、使用它们的角色(参见 Endpoint Roles)或通过 bean 注入手动启动这些端点。 您可以通过指定 XML 注释或 annotation 属性或通过调用 Bean 定义来显式覆盖此全局属性的效果。 从 4.3.12 版本开始。AbstractEndpointxxx*xxxxxx*yyyControl BusSmartLifecycleRoleControllerLifecycleauto-startupautoStartupAbstractEndpoint.setAutoStartup()
8 一个布尔标志,指示必须使用 option 配置 default global。 从 5.4.3 版本开始。 有关更多信息,请参阅错误处理errorChannelrequireSubscribers
9 一个布尔标志,指示 default global 必须忽略 dispatch 错误并将消息传递给下一个处理程序。 从 5.5 版本开始。errorChannel

可以通过将文件添加到 Classpath 或实例的 bean 来覆盖这些属性。 您无需提供所有属性,只需提供要覆盖的属性即可。/META-INF/spring.integration.propertiesIntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAMEorg.springframework.integration.context.IntegrationPropertiesspring-doc.cn

从版本 5.1 开始,当为类别打开逻辑电平时,所有合并的全局属性都会在应用程序上下文启动后打印在日志中。 输出如下所示:DEBUGorg.springframework.integrationspring-doc.cn

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 集成提供类级别作为原型注释,这意味着它本身使用 Spring 的注释进行注释,因此被 Spring 的组件扫描自动识别为 bean 定义。@MessageEndpoint@Componentspring-doc.cn

更重要的是各种方法级 Comments。 它们指示带注释的方法能够处理消息。 以下示例演示了类级和方法级 Comments:spring-doc.cn

@MessageEndpoint
public class FooService {

    @ServiceActivator
    public void processMessage(Message message) {
        ...
    }
}

“处理”Message 的方法的确切含义取决于特定的 Comments。 Spring Integration 中可用的 Comments 包括:spring-doc.cn

如果将 XML 配置与注释结合使用,则不需要注释。 如果要从元素的属性配置 POJO 引用,则只能提供方法级 Comments。 在这种情况下,即使元素上不存在方法级属性,注释也可以防止歧义。@MessageEndpointref<service-activator/><service-activator/>

在大多数情况下,带 Comments 的处理程序方法不应要求 type 作为其参数。 相反,method 参数类型可以与消息的有效负载类型匹配,如下例所示:Messagespring-doc.cn

public class ThingService {

    @ServiceActivator
    public void bar(Thing thing) {
        ...
    }

}

当 method 参数应从 中的值映射时,另一个选项是使用参数级注释。 通常,使用 Spring 集成注释注释的方法可以接受自身、消息有效负载或 Headers 值(带有)作为参数。 实际上,该方法可以接受组合,如下例所示:MessageHeaders@HeaderMessage@Headerspring-doc.cn

public class ThingService {

    @ServiceActivator
    public void otherThing(String payload, @Header("x") int valueX, @Header("y") int valueY) {
        ...
    }

}

您还可以使用注释将所有消息标头作为 ,如下例所示:@HeadersMapspring-doc.cn

public class ThingService {

    @ServiceActivator
    public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}
注释的值也可以是 SpEL 表达式(例如, ),当您希望在注入 Headers 值之前对其进行操作时,这非常有用。 它还提供了一个可选属性,用于指定属性值是否必须在标头中可用。 该属性的默认值为 .someHeader.toUpperCase()requiredrequiredtrue

对于其中几个注释,当消息处理方法返回非 null 值时,终端节点会尝试发送回复。 这在两个配置选项(命名空间和注释)中是一致的,因为使用此类终端节点的输出通道(如果可用),并且消息头值用作回退。REPLY_CHANNELspring-doc.cn

端点上的输出通道和回复通道消息头的组合支持管道方法,其中多个组件有一个输出通道,最终组件允许将回复消息转发到回复通道(如原始请求消息中指定)。 换句话说,最终组件取决于原始发送方提供的信息,并且可以动态支持任意数量的客户端。 这是返回地址模式的一个示例。

除了此处显示的示例之外,这些注释还支持 and 属性,如下例所示:inputChanneloutputChannelspring-doc.cn

@Service
public class ThingService {

    @ServiceActivator(inputChannel="input", outputChannel="output")
    public void otherThing(String payload, @Headers Map<String, Object> headerMap) {
        ...
    }

}

这些 Comments 的处理将创建与相应的 XML 组件相同的 bean——实例和实例(或入站通道适配器的实例)。 请参见 @Bean 方法上的注释。 Bean 名称由以下模式生成: . 在前面的示例中,bean 名称是 和 相同的名称,但 () bean 带有附加的 () 后缀。 可以使用注释和这些消息传递注释来自定义此类名称。 实例 ( instances) 也有资格被消息历史记录跟踪。AbstractEndpointMessageHandlerMessageSource[componentName].[methodName].[decapitalizedAnnotationClassShortName]thingService.otherThing.serviceActivatorAbstractEndpoint.handler.sourceMessageHandlerMessageSource@EndpointIdMessageHandlerMessageSourcespring-doc.cn

从版本 4.0 开始,所有消息传递注释都提供了选项 ( 和 ),以允许对应用程序上下文初始化进行终端节点生命周期控制。 它们分别默认为 和 。 要更改端点的状态(例如 或 ),可以使用(或自动装配)获取对端点 Bean 的引用并调用方法。 或者,您可以向 发送命令消息(请参阅 Control Bus)。 为此,您应该使用上一段前面提到的。SmartLifecycleautoStartupphasetrue0start()stop()BeanFactoryControl BusbeanNamespring-doc.cn

在解析上述注释后自动创建的通道(当没有配置特定的通道 bean 时)和相应的消费者端点,在上下文初始化结束时被声明为 bean。 这些 bean 可以在其他服务中自动装配,但是必须用 Comments 标记它们,因为在正常的自动装配处理期间,定义通常还不可用。@Lazyspring-doc.cn

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

@Bean
Thing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannelAdapter someInboundAdapter) {
    ...
}

从版本 6.0 开始,现在所有的消息传递注释都是,因此可以在同一个服务方法上声明几个相同类型的注释,这意味着创建与这些注释重复的端点一样多的端点:@Repeatablespring-doc.cn

@Transformer(inputChannel = "inputChannel1", outputChannel = "outputChannel1")
@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel2")
public String transform(String input) {
    return input.toUpperCase();
}

使用注释@Poller

在 Spring Integration 4.0 之前,消息传递注释要求 be 对 . 例如,需要一个元素来配置复合端点并使复合端点成为 . 版本 4.0 引入了注释,以允许直接在消息传递注释上配置属性,如下例所示:inputChannelSubscribableChannelPollableChannel<int:bridge/><int:poller/>PollingConsumer@Pollerpollerspring-doc.cn

public class AnnotationService {

    @Transformer(inputChannel = "input", outputChannel = "output",
        poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}"))
    public String handle(String payload) {
        ...
    }
}

注释仅提供简单选项。 您可以使用属性占位符配置注释的属性 (、 、 和 )。 此外,从版本 5.1 开始,还提供了 s 的选项。 如果需要提供更多轮询选项(例如,、 等),则应将 配置为通用 Bean 并使用其 Bean 名称作为 的 属性。 在这种情况下,不允许使用其他属性(必须在 Bean 上指定它们)。 请注意,如果配置了 a 且 no ,则使用默认值(如果它存在于应用程序上下文中)。 要使用 annotation 声明默认 Poller,请使用类似于以下示例的代码:@PollerPollerMetadata@PollermaxMessagesPerPollfixedDelayfixedRatecronreceiveTimeoutPollingConsumertransactionadvice-chainerror-handlerPollerMetadata@PollervaluePollerMetadatainputChannelPollableChannel@PollerPollerMetadata@Configurationspring-doc.cn

@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
    PollerMetadata pollerMetadata = new PollerMetadata();
    pollerMetadata.setTrigger(new PeriodicTrigger(10));
    return pollerMetadata;
}

下面的示例展示了如何使用默认 Poller:spring-doc.cn

public class AnnotationService {

    @Transformer(inputChannel = "aPollableChannel", outputChannel = "output")
    public String handle(String payload) {
        ...
    }
}

下面的示例展示了如何使用命名的 Poller:spring-doc.cn

@Bean
public PollerMetadata myPoller() {
    PollerMetadata pollerMetadata = new PollerMetadata();
    pollerMetadata.setTrigger(new PeriodicTrigger(1000));
    return pollerMetadata;
}

以下示例显示了使用默认 Poller 的终端节点:spring-doc.cn

public class AnnotationService {

    @Transformer(inputChannel = "aPollableChannel", outputChannel = "output"
                           poller = @Poller("myPoller"))
    public String handle(String payload) {
         ...
    }
}

从版本 4.3.3 开始,该 annotation 具有用于更轻松地配置底层 . 此属性与 XML 组件中的角色相同。 有关更多信息,请参阅终端节点命名空间支持@PollererrorChannelMessagePublishingErrorHandlererror-channel<poller>spring-doc.cn

消息注释上的属性与该属性互斥。 有关更多信息,请参阅下一节。poller()reactive()spring-doc.cn

使用注释@Reactive

这自 5.0 版以来一直存在,但仅当端点的 input 通道是(或任何实现)时才应用。 从版本 5.3 开始,当目标消息处理程序独立于 input 通道类型时,其实例也由框架创建。 从版本 5.5 开始,已为所有消息传递注释引入了 sub-annotation (类似于上面)。 它接受可选的 Bean 引用,并且独立于 input 通道类型和消息处理程序,将目标端点转换为实例。 该函数从运算符用于对 input 通道的反应式流源应用一些自定义(、、 等)。ReactiveStreamsConsumerFluxMessageChannelorg.reactivestreams.PublisherReactiveMessageHandler@Reactive@PollerFunction<? super Flux<Message<?>>, ? extends Publisher<Message<?>>>ReactiveStreamsConsumerFlux.transform()publishOn()doOnNext()log()retry()spring-doc.cn

以下示例演示如何将发布线程从独立于最终订阅者和生成方的 input 通道更改为该 input 通道:DirectChannelspring-doc.cn

@Bean
public Function<Flux<?>, Flux<?>> publishOnCustomizer() {
    return flux -> flux.publishOn(Schedulers.parallel());
}

@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer"))
public void handleReactive(String payload) {
    ...
}

消息注释上的属性与该属性互斥。 有关更多信息,请参阅使用 @Poller AnnotationReactive Streams 支持reactive()poller()spring-doc.cn

使用注释@InboundChannelAdapter

版本 4.0 引入了方法级 Comments。 它基于 a 为带注释的方法生成集成组件。 此注释是 XML 组件的类似物,具有相同的限制:该方法不能有参数,并且返回类型不能为 . 它有两个属性:(必需的 bean 名称)和(可选的 Comments,如前所述)。 如果需要提供一些 ,请使用 return 类型并使用 a 构建 . 使用 a 可以配置 . 以下示例演示如何使用批注:@InboundChannelAdapterSourcePollingChannelAdapterMethodInvokingMessageSource<int:inbound-channel-adapter>voidvalueMessageChannelpoller@PollerMessageHeadersMessage<?>MessageBuilderMessage<?>MessageBuilderMessageHeaders@InboundChannelAdapterspring-doc.cn

@InboundChannelAdapter("counterChannel")
public Integer count() {
    return this.counter.incrementAndGet();
}

@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000"))
public String foo() {
    return "foo";
}

版本 4.3 引入了 annotation 属性的别名,以提供更好的源代码可读性。 此外,目标 bean 在第一次调用时通过提供的名称(由选项设置)进行解析,而不是在初始化阶段。 它允许 “后期绑定” 逻辑:从消费者的角度来看,目标 bean 的创建和注册时间比解析阶段晚一点。channelvalueMessageChannelSourcePollingChannelAdapteroutputChannelNamereceive()MessageChannel@InboundChannelAdapterspring-doc.cn

第一个示例要求已在应用程序上下文中的其他位置声明默认 Poller。spring-doc.cn

使用注释@MessagingGatewayspring-doc.cn

使用注释@IntegrationComponentScan

标准 Spring Framework 注释不扫描接口的构造型 Comments。 为了克服此限制并允许配置(请参阅@MessagingGateway Annotation),我们引入了该机制。 此注释必须与注释一起放置,并对其进行自定义以定义其扫描选项。 例如 和 。 在这种情况下,所有发现的带注释的接口都将被解析并注册为实例。 所有其他基于 class 的组件都由标准 .@ComponentScan@Component@MessagingGateway@IntegrationComponentScan@ConfigurationbasePackagesbasePackageClasses@MessagingGatewayGatewayProxyFactoryBean@ComponentScanspring-doc.cn

消息传递元注释

从版本 4.0 开始,所有消息传递注释都可以配置为元注释,并且所有用户定义的消息传递注释都可以定义相同的属性来覆盖其默认值。 此外,可以按层次结构配置元注释,如下例所示:spring-doc.cn

@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 依赖项隔离到用户注释,从而避免在用户类中使用它们。 如果框架找到具有具有框架元注释的用户注释的方法,则会将其视为该方法直接使用框架注释进行注释。spring-doc.cn

方法上的注释@Bean

从版本 4.0 开始,您可以在类中的方法定义上配置消息传递注释,以基于 Bean 而不是方法生成消息端点。 当定义是“开箱即用”实例(、 、 和其他实例)、实例( 、 和其他实例)和实例( 、 和其他实例)时,它非常有用。 以下示例显示如何将消息收发注释与注释一起使用:@Bean@Configuration@BeanMessageHandlerAggregatingMessageHandlerDefaultMessageSplitterTransformerJsonToObjectTransformerClaimCheckOutTransformerMessageSourceFileReadingMessageSourceRedisStoreMessageSource@Beanspring-doc.cn

@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 引入了对 annotated with that returns 的支持,它可以生成 POJO 或 . 下面的示例演示如何使用该组合:@Bean@InboundChannelAdapterjava.util.function.SupplierMessagespring-doc.cn

@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@Beanspring-doc.cn

在消费者定义上使用这些注释时,如果 Bean 定义返回适当的(取决于注释类型),则必须在定义本身上设置属性(例如、和其他)。 仅使用以下注释属性:、 、 、 和 。 所有其他属性都用于处理程序。@BeanMessageHandleroutputChannelrequiresReplyorderMessageHandler@BeanadviceChainautoStartupinputChannelphasepoller
Bean 名称是使用以下算法生成的:
  • () 从 . 这就像方法上没有消息注释一样。MessageHandlerMessageSource@Beanname@Bean@Beanspring-doc.cn

  • Bean 名称使用以下模式生成: . 例如,前面显示的定义的端点的 bean 名称为 . 与 POJO 方法不同,端点 Bean 名称中不包含 Bean 方法名称。 另请参见端点 Bean 名称AbstractEndpoint[@Bean name].[decapitalizedAnnotationClassShortName]SourcePollingChannelAdapterconsoleSource()consoleSource.inboundChannelAdapterspring-doc.cn

  • 如果不能直接在目标端点中使用(不是 , 或 的实例),则相应地注册以委托给此 。 此包装器的 Bean 名称是使用以下模式生成的:.@BeanMessageSourceAbstractReplyProducingMessageHandlerAbstractMessageRouterAbstractStandardMessageHandlerFactoryBean@Bean[@Bean name].[decapitalizedAnnotationClassShortName].[handler (or source)]spring-doc.cn

在定义上使用这些 Comments 时,必须引用已声明的 Bean。 如果应用程序上下文中尚不存在 channels,则会自动声明 channel。@BeaninputChannel

使用 Java 配置,您可以在方法级别上使用任何(例如)定义来出于某些条件原因跳过 bean 注册。 以下示例显示了如何执行此操作:@Conditional@Profile@Beanspring-doc.cn

@Bean
@ServiceActivator(inputChannel = "skippedChannel")
@Profile("thing")
public MessageHandler skipped() {
    return System.out::println;
}

与现有的 Spring 容器逻辑一起,消息传递端点 Bean(基于 Comments)也没有注册。@ServiceActivatorspring-doc.cn

创建带有注释的桥接

从版本 4.0 开始,Java 配置提供了 and 方法注释来标记类中的 bean。 这些确实是为了完整性而存在的,提供了一种方便的机制来声明 a 及其消息端点配置:@BridgeFrom@BridgeTo@BeanMessageChannel@ConfigurationBridgeHandlerspring-doc.cn

@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-doc.cn

建议带注释的终端节点

消息映射规则和约定

Spring 集成通过依赖一些默认规则和定义某些约定,实现了一种灵活的工具,可以将消息映射到方法及其参数,而无需提供额外的配置。 以下各节中的示例阐明了这些规则。spring-doc.cn

示例场景

以下示例显示了一个未注释的参数(对象或基元),该参数不是 a 或具有非 void 返回类型的对象:MapPropertiesspring-doc.cn

public String doSomething(Object o);

input 参数是消息负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值将合并为返回消息的有效负载。spring-doc.cn

以下示例显示了一个未注释的参数(对象或基元),该参数不是具有返回类型的 a 或 a:MapPropertiesMessagespring-doc.cn

public Message doSomething(Object o);

input 参数是消息负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值是发送到下一个目标的新构造的消息。spring-doc.cn

下面的示例显示了一个参数,该参数是具有任意对象或原始返回类型的消息(或其子类之一):spring-doc.cn

public int doSomething(Message msg);

输入参数本身是一个 . 返回值将成为发送到下一个目标的 的有效负载。MessageMessagespring-doc.cn

下面的示例显示了一个参数,该参数是 a (或其子类之一) 作为返回类型:MessageMessagespring-doc.cn

public Message doSomething(Message msg);

输入参数本身是一个 . 返回值是发送到下一个目标的新构造值。MessageMessagespring-doc.cn

以下示例显示了 type 或 a 作为返回类型的单个参数:MapPropertiesMessagespring-doc.cn

public Message doSomething(Map m);

这个有点有趣。 尽管乍一看,直接映射到消息标头似乎很容易,但始终优先考虑有效负载。 这意味着,如果 payload 的类型为 ,则此 input 参数表示 payload。 但是,如果 payload 不是 type ,则转换服务不会尝试转换有效负载,并且 input 参数将映射到消息标头。MessageMessageMapMessageMessageMapspring-doc.cn

以下示例显示了两个参数,其中一个是不是 a 或 object 的任意类型 (对象或基元),另一个是 type 或 type (,无论返回结果如何) :MapPropertiesMapPropertiesspring-doc.cn

public Message doSomething(Map h, <T> t);

此组合包含两个输入参数,其中一个是 类型。 非参数(无论顺序如何)被映射到有效负载,or(无论顺序如何)被映射到消息头,从而为您提供一种与结构交互的良好 POJO 方式。MapMapMessageMapPropertiesMessagespring-doc.cn

以下示例显示无参数(无论返回结果如何):spring-doc.cn

public String doSomething();

此消息处理程序方法是根据发送到此处理程序所连接的输入通道的 Message 调用的。 但是,没有映射任何数据,因此 充当事件或触发器来调用处理程序。 根据前面描述的规则映射输出。MessageMessagespring-doc.cn

以下示例显示无参数和 void 返回:spring-doc.cn

public void soSomething();

此示例与上一个示例相同,但它不会生成任何输出。spring-doc.cn

基于注释的映射

基于 Comments 的映射是将消息映射到方法的最安全且最不明确的方法。 下面的示例演示如何将方法显式映射到标头:spring-doc.cn

public String doSomething(@Payload String s, @Header("someheader") String b)

正如您稍后所看到的,如果没有 Annotation,此签名将导致模棱两可的条件。 但是,通过将第一个参数显式映射到有效负载,将第二个参数显式映射到消息标头的值,我们可以避免任何歧义。Messagesomeheaderspring-doc.cn

以下示例与前面的示例几乎相同:spring-doc.cn

public String doSomething(@Payload String s, @RequestParam("something") String b)

@RequestMapping或任何其他非 Spring 集成映射注释都是无关紧要的,因此被忽略,使第二个参数未映射。 尽管第二个参数可以很容易地映射到有效负载,但只能有一个有效负载。 因此,注释使此方法不明确。spring-doc.cn

以下示例显示了另一种类似的方法,如果不是 annotation 来阐明意图,该方法将是模棱两可的:spring-doc.cn

public String foo(String s, @Header("foo") String b)

唯一的区别是第一个参数隐式映射到消息有效负载。spring-doc.cn

以下示例显示了另一个签名,如果没有注释,该签名肯定会被视为不明确,因为它具有两个以上的参数:spring-doc.cn

public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar)

这个例子尤其有问题,因为它的两个参数是实例。 但是,使用基于注释的映射,可以轻松避免歧义。 在此示例中,第一个参数映射到所有消息标头,而第二个和第三个参数映射到名为 'something' 和 'someotherthing' 的消息标头的值。 有效负载未映射到任何参数。Mapspring-doc.cn

复杂场景

以下示例使用多个参数:spring-doc.cn

多个参数可能会在确定适当的映射时产生很多歧义。 一般建议用 、 和 来注释您的方法参数。 本节中的示例显示了导致引发异常的不明确条件。@Payload@Header@Headersspring-doc.cn

public String doSomething(String s, int i)

这两个参数的权重相等。 因此,无法确定哪一个是有效负载。spring-doc.cn

以下示例显示了一个类似的问题,只有三个参数:spring-doc.cn

public String foo(String s, Map m, String b)

尽管 Map 可以很容易地映射到消息头,但无法确定如何处理这两个 String 参数。spring-doc.cn

以下示例显示了另一种不明确的方法:spring-doc.cn

public String foo(Map m, Map f)

尽管有人可能会争辩说,一个可以映射到消息有效负载,另一个可以映射到消息标头,但我们不能依赖顺序。Mapspring-doc.cn

任何具有多个非 (, ) 且具有未注释参数的方法签名都会导致不明确的情况并触发异常。Map<T>

下一组示例分别显示了导致歧义的多种方法。spring-doc.cn

具有多个方法的消息处理程序根据前面描述的相同规则进行映射(在示例中)。 但是,某些方案可能仍然看起来令人困惑。spring-doc.cn

以下示例显示了具有合法(可映射和明确)签名的多种方法:spring-doc.cn

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(Map m);
}

(方法具有相同的名称或不同的名称没有区别)。 可以映射到任一方法。 当消息有效负荷可以映射到 并且消息报头可以映射到 时,将调用第一种方法。 第二种方法也可以是候选方法,只需将消息报头映射到 . 更糟糕的是,这两种方法具有相同的名称。 起初,由于以下配置,这可能看起来模棱两可:Messagestrmmspring-doc.cn

<int:service-activator input-channel="input" output-channel="output" method="doSomething">
    <bean class="org.things.Something"/>
</int:service-activator>

它之所以有效,是因为 Map 首先基于有效负载,然后基于其他所有内容。 换句话说,其第一个参数可以映射到有效负载的方法优先于所有其他方法。spring-doc.cn

现在考虑另一个示例,它会产生一个真正模棱两可的条件:spring-doc.cn

public class Something {
    public String doSomething(String str, Map m);

    public String doSomething(String str);
}

这两种方法都具有可以映射到消息有效负载的签名。 它们也具有相同的名称。 此类处理程序方法将触发异常。 但是,如果方法名称不同,则可以使用属性影响映射(如下一个示例所示)。 以下示例显示了具有两个不同方法名称的相同示例:methodspring-doc.cn

public class Something {
    public String doSomething(String str, Map m);

    public String doSomethingElse(String str);
}

下面的示例演示如何使用 attribute 来指示映射:methodspring-doc.cn

<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse">
    <bean class="org.bar.Foo"/>
</int:service-activator>

因为配置显式地映射了方法,所以我们消除了歧义。doSomethingElsespring-doc.cn