配置

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

命名空间支持

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

要在 Spring 配置文件中启用 Spring Integration 的核心命名空间支持,请在顶级“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 Integration 模式作为主要命名空间的配置文件,如以下示例所示:

<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 Integration 元素不需要前缀。另一方面,如果您在同一个配置文件中定义一个通用 Spring bean,则 bean 元素需要一个前缀 ( <beans:bean …​/>)。由于模块化配置文件本身通常是一个好主意(基于责任或架构层),您可能会发现在以集成为中心的配置文件中使用后一种方法是合适的,因为在这些文件中很少需要通用 bean。出于本文档的目的,我们假设集成命名空间是主要的。

Spring Integration 提供了许多其他命名空间。事实上,每个提供命名空间支持的适配器类型(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-rmi="http://www.springframework.org/schema/integration/rmi"
  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/rmi
    https://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd
    http://www.springframework.org/schema/integration/ws
    https://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
 ...
</beans>

本参考手册在其相应章节中提供了各种元素的具体示例。在这里,要认识到的主要是每个命名空间 URI 和模式位置的命名一致性。

配置任务计划程序

在 Spring Integration 中,ApplicationContext消息总线扮演着核心角色,您只需要考虑几个配置选项。首先,您可能想要控制中央TaskScheduler实例。您可以通过提供一个名为taskScheduler. 这也被定义为一个常数,如下:

IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME

默认情况下,Spring Integration 依赖于 的实例ThreadPoolTaskScheduler,如 Spring Framework 参考手册的任务执行和调度部分所述。该默认设置TaskScheduler会自动启动一个包含 10 个线程的池,但请参阅Global Properties。如果您提供自己的TaskScheduler实例,则可以将“autoStartup”属性设置为false或提供您自己的池大小值。

当轮询消费者在其配置中提供明确的任务执行器引用时,处理程序方法的调用发生在该执行器的线程池中,而不是主调度程序池中。但是,当没有为端点的轮询器提供任务执行器时,它会由主调度器的线程之一调用。

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

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

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

有关详细信息,另请参阅错误处理

全局属性

通过在类路径上提供属性文件可以覆盖某些全局框架属性。

默认属性可以在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 设置允许的默认订阅者数量,例如 a DirectChannel。它可用于避免无意中将多个端点订阅到同一通道。max-subscribers您可以通过设置属性在单个通道上覆盖它。
3 此属性提供允许的默认订阅者数量,例如 a PublishSubscribeChannel。它可用于避免无意中订阅超过预期数量的端点到同一通道。max-subscribers您可以通过设置属性在单个通道上覆盖它。
4 taskScheduler默认bean中可用的线程数。请参阅配置任务计划程序
5 何时true,到达网关回复通道的消息在网关不期待回复时抛出异常(因为发送线程已超时或已收到回复)。
6 在标头复制操作期间不应填充到Message实例中的消息标头名称的逗号分隔列表。该列表由DefaultMessageBuilderFactorybean 使用并传播到用于构建消息的实例(请参阅 API)IntegrationMessageHeaderAccessorMessageHeaderAccessor参阅MessageBuilderHelper)。默认情况下,仅在消息构建期间不复制。从 4.3.2 版开始。MessageBuilderMessageHeaders.IDMessageHeaders.TIMESTAMP
7 在应用程序启动期间不应自动启动的AbstractEndpointbean 名称模式(、、或xxx*xxx*xxx逗号分隔列表。xxx*yyy您可以稍后通过它们的 bean 名称通过Control Bus(请参阅控制总线)、通过它们的角色SmartLifecycleRoleController(请参阅端点角色)或通过Lifecyclebean 注入手动启动这些端点。auto-startup您可以通过指定XML 注释或autoStartup注释属性或调用AbstractEndpoint.setAutoStartup()bean 定义来显式覆盖此全局属性的效果。从 4.3.12 版开始。
8 一个布尔标志,指示errorChannel必须使用该requireSubscribers选项配置默认全局。从 5.4.3 版开始。有关详细信息,请参阅错误处理
9 一个布尔标志,指示默认全局errorChannel必须忽略调度错误并将消息传递给下一个处理程序。从 5.5 版开始。

/META-INF/spring.integration.properties可以通过将文件添加到类路径或实例的IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAMEbean来覆盖这些属性。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 定义。

更重要的是各种方法级别的注释。它们表明带注释的方法能够处理消息。以下示例演示了类级别和方法级别的注释:

@MessageEndpoint
public class FooService {

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

方法“处理”消息的确切含义取决于特定的注释。Spring Integration 中可用的注解包括:

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

在大多数情况下,带注释的处理程序方法不应要求Message类型作为其参数。相反,方法参数类型可以匹配消息的有效负载类型,如以下示例所示:

public class ThingService {

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

}

当方法参数应该从 中的值映射时MessageHeaders,另一种选择是使用参数级@Header注释。通常,使用 Spring Integration annotations 注释的方法可以接受Message自身、消息有效负载或标头值 (with @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()),当您希望在注入前操纵标头值时,这很有用。它还提供了一个可选required属性,它指定属性值是否必须在标头中可用。该required属性的默认值为true

对于其中一些注释,当消息处理方法返回非空值时,端点会尝试发送回复。这在两个配置选项(命名空间和注释)中是一致的,因为使用了这样的端点的输出通道(如果可用),并且REPLY_CHANNEL消息头值用作后备。

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

除了此处显示的示例之外,这些注释还支持inputChanneloutputChannel属性,如以下示例所示:

@Service
public class ThingService {

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

}

这些注释的处理创建了与相应的 XML 组件相同的 bean — AbstractEndpoint实例和MessageHandler实例(或MessageSource入站通道适配器的实例)。请参阅方法注释@Bean。bean 名称是从以下模式生成的:[componentName].[methodName].[decapitalizedAnnotationClassShortName]. 在前面的示例中,bean 名称与( ) beanthingService.otherThing.serviceActivator的名称AbstractEndpoint相同,附加.handler( .source) 后缀。可以使用注释以及这些消息注释来自定义这样的名称。实例(instances)也有资格被消息历史记录跟踪。MessageHandlerMessageSource@EndpointIdMessageHandlerMessageSource

从版本 4.0 开始,所有消息注释都提供SmartLifecycle选项 (autoStartupphase) 以允许对应用程序上下文初始化进行端点生命周期控制。它们分别默认为true0。要更改端点的状态(例如 `start()` 或stop()),您可以使用(或自动装配)获取对端点 bean 的引用BeanFactory并调用方法。或者,您可以将命令消息发送到Control Bus(请参阅控制总线)。出于这些目的,您应该使用beanName前一段中提到的内容。

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

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

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

使用@Poller注解

在 Spring Integration 4.0 之前,消息注释要求是inputChannelSubscribableChannel. 例如PollableChannel,需要一个<int:bridge/>元素来配置 an<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注释只提供简单的PollerMetadata选项。您可以使用属性占位符配置@Poller注释的属性(maxMessagesPerPollfixedDelayfixedRate和)。cron此外,从 5.1 版开始,还提供receiveTimeout了 s 选项。PollingConsumer如果需要提供更多轮询选项(例如 、、transaction等),则应将 配置为通用 bean 并将其 bean 名称用作'属性。在这种情况下,不允许使用其他属性(它们必须在bean 上指定)。注意,如果是a并且没有配置,默认advice-chainerror-handlerPollerMetadata@PollervaluePollerMetadatainputChannelPollableChannel@PollerPollerMetadata被使用(如果它存在于应用程序上下文中)。要使用注解声明默认轮询器@Configuration,请使用类似于以下示例的代码:

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

以下示例显示了如何使用默认轮询器:

public class AnnotationService {

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

以下示例显示了如何使用命名轮询器:

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

以下示例显示了使用默认轮询器的端点:

public class AnnotationService {

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

从版本 4.3.3 开始,@Poller注解具有errorChannel更容易配置底层MessagePublishingErrorHandler. error-channel此属性的作用与<poller>XML 组件中的作用相同。有关更多信息,请参阅端点命名空间支持

消息注释上的属性poller()与属性互斥reactive()。有关详细信息,请参阅下一节。

使用@Reactive注解

ReactiveStreamsConsumer5.0 版以来一直存在,但仅当端点的输入通道是 a FluxMessageChannel(或任何org.reactivestreams.Publisher实现)时才应用它。ReactiveMessageHandler从 5.3 版开始,当目标消息处理程序独立于输入通道类型时,它的实例也由框架创建。从5.5 版本开始,所有消息注释都引入了@Reactive子注释(类似于上面提到的)。@Poller它接受一个可选的Function<? super Flux<Message<?>>, ? extends Publisher<Message<?>>>bean 引用,并且独立于输入通道类型和消息处理程序,将目标端点转换为ReactiveStreamsConsumer实例。操作员使用该功能Flux.transform()来应用一些自定义(publishOn(), doOnNext(), log(),retry()等)在来自输入通道的反应流源上。

以下示例演示了如何将发布线程从独立于最终订阅者和生产者的输入通道更改为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注释反应式流支持

使用@InboundChannelAdapter注解

4.0 版引入了@InboundChannelAdapter方法级注解。SourcePollingChannelAdapter它基于MethodInvokingMessageSource注解方法生成一个集成组件。此注解类似于<int:inbound-channel-adapter>XML 组件,具有相同的限制:方法不能有参数,返回类型不能是void. 它有两个属性:(value必需的MessageChannelbean 名称)和poller(可选的@Poller注解,如前所述)。如果您需要提供一些MessageHeaders,请使用Message<?>返回类型并使用 aMessageBuilder来构建Message<?>. 使用 aMessageBuilder可以配置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,以提供更好的源代码可读性。此外,目标bean 在第一次调用时由提供的名称(由选项设置)MessageChannel解析,而不是在初始化阶段。它允许“后期绑定”逻辑:从消费者的角度来看,目标bean 的创建和注册比解析阶段晚一点。SourcePollingChannelAdapteroutputChannelNamereceive()MessageChannel@InboundChannelAdapter

第一个示例要求默认轮询器已在应用程序上下文的其他地方声明。

使用@MessagingGateway注解

使用@IntegrationComponentScan注解

标准的 Spring Framework@ComponentScan注解不会扫描接口的构造型@Component注解。为了克服这个限制并允许配置@MessagingGateway(参见@MessagingGatewayAnnotation),我们引入了该@IntegrationComponentScan机制。此注释必须与@Configuration注释一起放置并自定义以定义其扫描选项,例如basePackagesbasePackageClasses。在这种情况下,所有发现的带有注释的接口@MessagingGateway都被解析并注册为GatewayProxyFactoryBean实例。所有其他基于类的组件都由标准解析@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实例(AggregatingMessageHandlerDefaultMessageSplitter等)、Transformer实例(JsonToObjectTransformerClaimCheckOutTransformer等)和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 MessageHandler 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

当您在消费者@Bean定义上使用这些注解时,如果 bean 定义返回适当的MessageHandler(取决于注解类型),您必须在定义本身上设置属性(例如outputChannelrequiresReplyorder等) 。MessageHandler @Bean仅使用以下注释属性:adviceChainautoStartupinputChannelphasepoller。所有其他属性都用于处理程序。
bean 名称是使用以下算法生成的:
  • MessageHandler( MessageSource)@Bean从. _ name_ @Bean这就像@Bean方法上没有消息注释一样工作。

  • bean 名称使用AbstractEndpoint以下模式生成:[configurationComponentName].[methodName].[decapitalizedAnnotationClassShortName]. 例如,前面显示的定义的SourcePollingChannelAdapter端点获得一个 bean 名称。另请参见端点 Bean 名称consoleSource()myFlowConfiguration.consoleSource.inboundChannelAdapter

@Bean定义上使用这些注释时,inputChannel必须引用已声明的 bean。在这种情况下,通道不会自动声明。

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

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

与现有的 Spring 容器逻辑一起,消息传递端点 bean(基于@ServiceActivator注释)也没有注册。

创建带有注释的桥

从 4.0 版开始,Java 配置提供了@BridgeFrom@BridgeTo @Bean方法注解来标记类中的MessageChannelbean @Configuration。这些确实是为了完整性而存在,提供了一种方便的机制来声明 aBridgeHandler及其消息端点配置:

@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 Integration 通过依赖一些默认规则和定义某些约定,实现了一种灵活的工具来将消息映射到方法及其参数,而无需提供额外的配置。以下部分中的示例阐明了这些规则。

示例场景

以下示例显示了一个未注释的参数(对象或原语),它不是 aMapProperties具有非 void 返回类型的对象:

public String doSomething(Object o);

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

以下示例显示了一个未注释的参数(对象或原语),它不是 aMapProperties具有Message返回类型的 a:

public Message doSomething(Object o);

输入参数是消息负载。如果参数类型与消息负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。返回值是一个新构造的消息,它被发送到下一个目的地。

以下示例显示了一个参数,它是具有任意对象或原始返回类型的消息(或其子类之一):

public int doSomething(Message  msg);

输入参数本身就是 a Message。返回值成为Message发送到下一个目的地的有效负载。

以下示例显示了一个参数,它是 a Message(或其子类Message之一),其返回类型为 a(或其子类之一):

public Message doSomething(Message msg);

输入参数本身就是 a Message。返回值是一个新构造Message的,被发送到下一个目的地。

以下示例显示了单个参数类型MapProperties以 aMessage作为返回类型:

public Message doSomething(Map m);

这个有点意思。虽然,起初,这似乎是直接映射到消息头的简单方法,但总是优先考虑Message有效负载。这意味着如果Message有效负载的类型为Map,则此输入参数表示Message有效负载。但是,如果Message负载不是 type Map,则转换服务不会尝试转换负载,并且输入参数将映射到消息头。

下面的示例显示了两个参数,其中一个是任意类型(对象或基元),不是 aMapProperties对象,另一个是类型MapProperties类型(不管返回):

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

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

以下示例不显示任何参数(无论返回如何):

public String doSomething();

此消息处理程序方法基于发送到此处理程序所连接的输入通道的消息来调用。然而,没有Message数据被映射,因此使Message行为作为事件或触发器来调用处理程序。根据前面描述的规则映射输出。

以下示例显示无参数和 void 返回:

public void soSomething();

此示例与上一个示例相同,但它不产生任何输出。

基于注释的映射

基于注释的映射是将消息映射到方法的最安全和最不模糊的方法。以下示例显示如何将方法显式映射到标头:

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

正如您稍后会看到的,如果没有注释,此签名将导致模棱两可的情况。但是,通过将第一个参数显式映射到Message有效负载,将第二个参数显式映射到someheader消息头的值,我们避免了任何歧义。

以下示例与前面的示例几乎相同:

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

@RequestMapping或任何其他非 Spring Integration 映射注释是不相关的,因此被忽略,使第二个参数未映射。尽管第二个参数可以很容易地映射到有效负载,但只能有一个有效负载。因此,注释使这种方法不会模棱两可。

以下示例显示了另一种类似的方法,如果没有注释来阐明意图,则该方法将是模棱两可的:

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可以映射到消息负载,另一个映射到消息头,但我们不能依赖顺序。

任何具有多个方法参数但不是 (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>

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

现在考虑一个替代示例,它产生了一个真正模棱两可的条件:

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方法,所以我们消除了歧义。


1. see XML Configuration