服务激活器

服务激活器是将任何 Spring 托管对象连接到输入通道的端点类型,以便它可以充当服务的角色。如果服务产生输出,它也可以连接到输出通道。或者,生成输出的服务可以位于处理管道或消息流的末端,在这种情况下,可以使用入站消息的replyChannel 标头。如果未定义输出通道,则这是默认行为。与此处描述的大多数配置选项一样,相同的行为实际上适用于大多数其他组件。

服务激活器本质上是一个通用端点,用于使用输入消息(有效负载和标头)调用某个对象上的方法。其内部逻辑基于一个MessageHandler,它可以是特定用例的任何可能的实现,例如DefaultMessageSplitterAggregatingMessageHandlerSftpMessageHandlerJpaOutboundGateway 等。因此,本参考手册中提到的任何出站网关和出站通道适配器都应视为此服务激活器端点的特定扩展;它们最终都会调用某个对象的方法。

配置服务激活器

使用 Java 和注解配置,只需使用@ServiceActivator 注解标记相应的服务方法 - 框架会在从输入通道使用消息时调用它。

public class SomeService {

    @ServiceActivator(inputChannel = "exampleChannel")
    public void exampleHandler(SomeData payload) {
        ...
    }

}

有关更多信息,请参阅注解支持

对于 Java、Groovy 或 Kotlin DSL,IntegrationFlow.handle() 操作符表示一个服务激活器。

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

@Bean
public IntegrationFlow someFlow() {
    return IntegrationFlow
             .from("exampleChannel")
             .handle(someService, "exampleHandler")
             .get();
}
@Bean
fun someFlow() =
    integrationFlow("exampleChannel") {
        handle(someService, "exampleHandler")
    }
@Bean
someFlow() {
    integrationFlow 'exampleChannel',
            {
                handle someService, 'exampleHandler'
            }
}

有关 DSL 的更多信息,请参阅各个章节。

在使用 XML 配置时创建服务激活器,请使用带“input-channel”和“ref”属性的“service-activator”元素,如下例所示。

<int:service-activator input-channel="exampleChannel" ref="exampleHandler"/>

前面的配置选择来自exampleHandler 的所有满足以下消息传递要求之一的方法:

  • @ServiceActivator 注解。

  • public

  • 如果requiresReply == true,则不返回void

在运行时调用的目标方法是根据其payload 类型为每个请求消息选择的,或者如果目标类上存在此类方法,则作为回退到Message<?> 类型。

从 5.0 版开始,可以将一个服务方法标记为@org.springframework.integration.annotation.Default 作为所有不匹配情况的回退。当使用内容类型转换 并将在转换后调用目标方法时,这将很有用。

要委托给任何对象的明确定义的方法,您可以添加method 属性,如下例所示。

<int:service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/>

在任何一种情况下,当服务方法返回非空值时,端点都会尝试将回复消息发送到相应的回复通道。为了确定回复通道,它首先检查在端点配置中是否提供了output-channel,如下例所示。

<int:service-activator input-channel="exampleChannel" output-channel="replyChannel"
                       ref="somePojo" method="someMethod"/>

如果方法返回结果且未定义output-channel,则框架会检查请求消息的replyChannel 标头值。如果该值可用,则会检查其类型。如果是MessageChannel,则将回复消息发送到该通道。如果是String,则端点会尝试将通道名称解析为通道实例。如果无法解析通道,则会抛出DestinationResolutionException。如果可以解析,则消息将发送到那里。如果请求消息没有replyChannel 标头并且reply 对象是Message,则会查询其replyChannel 标头以获取目标目的地。这是 Spring Integration 中用于请求-回复消息传递的技术,它也是返回地址模式的一个示例。

如果您的方法返回结果,并且您想丢弃它并结束流程,则应将output-channel 配置为发送到NullChannel。为方便起见,框架会注册一个名为nullChannel 的通道。有关更多信息,请参阅特殊通道

服务激活器是不需要生成回复消息的组件之一。如果您的方法返回null 或具有void 返回类型,则服务激活器在方法调用后退出,没有任何信号。此行为可以通过AbstractReplyProducingMessageHandler.requiresReply 选项控制,在使用 XML 命名空间配置时,它也显示为requires-reply。如果将该标志设置为true 并且方法返回 null,则会抛出ReplyRequiredException

服务方法中的参数可以是消息或任意类型。如果是后者,则假定它是从消息中提取并注入到服务方法中的消息有效负载。我们通常推荐这种方法,因为它遵循并在使用 Spring Integration 时促进了 POJO 模型。参数也可以具有@Header@Headers 注解,如注解支持 中所述。

服务方法不需要任何参数,这意味着您可以实现事件样式的服务激活器(您只关心服务方法的调用),而不必担心消息的内容。将其视为一个空 JMS 消息。此类实现的一个示例用例是对输入通道上存放的消息进行简单的计数或监控。

从 4.1 版开始,框架正确地将消息属性(payloadheaders)转换为 Java 8 Optional POJO 方法参数,如下例所示。

public class MyBean {
    public String computeValue(Optional<String> payload,
               @Header(value="foo", required=false) String foo1,
               @Header(value="foo") Optional<String> foo2) {
        if (payload.isPresent()) {
            String value = payload.get();
            ...
        }
        else {
           ...
       }
    }

}

如果自定义服务激活器处理程序实现可以在其他<service-activator> 定义中重复使用,我们通常建议使用ref 属性。但是,如果自定义服务激活器处理程序实现仅在<service-activator> 的单个定义中使用,则可以提供内部 bean 定义,如下例所示。

<int:service-activator id="exampleServiceActivator" input-channel="inChannel"
            output-channel = "outChannel" method="someMethod">
    <beans:bean class="org.something.ExampleServiceActivator"/>
</int:service-activator>
在同一个<service-activator> 配置中同时使用ref 属性和内部处理程序定义是不允许的,因为它会创建模棱两可的条件并导致抛出异常。
如果ref 属性引用扩展AbstractMessageProducingHandler 的 bean(例如框架本身提供的处理程序),则配置会通过将输出通道直接注入处理程序来进行优化。在这种情况下,每个ref 必须指向单独的 bean 实例(或prototype 范围的 bean)或使用内部<bean/> 配置类型。如果您无意中从多个 bean 引用相同的邮件处理程序,则会收到配置异常。

服务激活器和 Spring 表达式语言 (SpEL)

从 Spring Integration 2.0 开始,服务激活器也可以从SpEL 中获益。

例如,您可以调用任何 bean 方法,而无需在ref 属性中指向 bean 或将其包含为内部 bean 定义,如下所示。

<int:service-activator input-channel="in" output-channel="out"
	expression="@accountService.processAccount(payload, headers.accountId)"/>

	<bean id="accountService" class="thing1.thing2.Account"/>

在前面的配置中,我们使用 SpEL 的@beanId 表示法调用一个方法,该方法采用与消息有效负载兼容的类型,而不是通过使用ref 或作为内部 bean 来注入“accountService”。我们还传递了一个标头值。任何有效的 SpEL 表达式都可以针对消息中的任何内容进行评估。对于简单的场景,如果所有逻辑都可以在此类表达式中封装,则您的服务激活器不需要引用 bean,如下例所示。

<int:service-activator input-channel="in" output-channel="out" expression="payload * 2"/>

在前面的配置中,我们的服务逻辑是将有效负载值乘以 2。SpEL 使我们能够相对轻松地处理它。

有关配置服务激活器的更多信息,请参阅 Java DSL 章节中的服务激活器和.handle() 方法

异步服务激活器

服务激活器由调用线程调用。如果输入通道是SubscribableChannel 或轮询通道的轮询线程,则此线程是上游线程。如果服务返回CompletableFuture<?>,则默认操作是将其作为发送到输出(或回复)通道的消息的有效负载发送。从 4.3 版开始,您现在可以将async 属性设置为true(在使用 Java 配置时使用setAsync(true))。如果服务在将此async 属性设置为true 时返回CompletableFuture<?>,则会立即释放调用线程,并且回复消息会在完成未来的线程(来自您的服务)上发送。这对于使用PollableChannel 的长时间运行的服务尤其有利,因为轮询线程会被释放以在框架内执行其他服务。

如果服务使用Exception 完成未来,则会发生正常的错误处理。如果存在,则会将ErrorMessage 发送到errorChannel 消息标头。否则,会将ErrorMessage 发送到默认的errorChannel(如果可用)。

从 6.1 版开始,如果AbstractMessageProducingHandler 的输出通道配置为ReactiveStreamsSubscribableChannel,则默认情况下会打开异步模式。如果处理程序结果不是反应式类型或CompletableFuture<?>,则即使输出通道类型,也会发生常规的回复生成过程。

有关更多信息,另请参阅Reactive Streams 支持

服务激活器和方法返回类型

服务方法可以返回任何类型,该类型成为回复消息的有效负载。在这种情况下,会创建一个新的Message<?> 对象,并复制请求消息中的所有标头。当交互基于 POJO 方法调用时,这对于大多数 Spring Integration MessageHandler 实现的工作方式相同。

方法也可以返回完整的Message<?>对象。但是,请记住,与转换器不同,对于服务激活器,如果返回的消息中不存在请求消息的报头,则此消息将通过复制请求消息的报头来修改。因此,如果您的方法参数是Message<?>,并且您在服务方法中复制了一些(但不是全部)现有的报头,则它们将重新出现在回复消息中。服务激活器不负责从回复消息中删除报头,并且为了遵循松耦合原则,最好在集成流中添加一个HeaderFilter。或者,可以使用转换器而不是服务激活器,但在这种情况下,当返回完整的Message<?>时,方法完全负责消息,包括复制请求消息报头(如果需要)。您必须确保重要的框架报头(例如replyChannelerrorChannel)如果存在,必须保留。