跳到主要内容

服务激活器

QWen Plus 中英对照 Service Activator

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

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

配置 Service Activator

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

public class SomeService {

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

}
java

请在 注解支持 中查看更多信息。

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

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

有关DSL的更多信息,请参见相关章节:

在使用 XML 配置创建服务激活器时,使用带有 input-channelref 属性的 'service-activator' 元素,如下例所示:

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

上述配置选择了 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"/>
xml

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

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

如果方法返回结果且未定义 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 {
...
}
}

}
java

我们通常建议使用 ref 属性,如果自定义服务激活器处理器实现可以在其他 <service-activator> 定义中重复使用。然而,如果自定义服务激活器处理器实现仅在单个 <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>
xml
备注

在同一 <service-activator> 配置中使用 ref 属性和内部处理器定义是不允许的,因为它会创建一个模糊的条件,并导致抛出异常。

important

如果 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"/>
xml

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

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

在前面的配置中,我们的服务逻辑是将有效负载值乘以二。SpEL 让我们相对容易地处理它。

有关配置服务激活器的更多信息,请参阅 Java DSL 章节中的 Service Activators and the .handle() method

异步服务激活器

服务激活器由调用线程调用。如果输入通道是 SubscribableChannel,则这是一个上游线程,或者是 PollableChannel 的轮询线程。如果服务返回一个 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<?> 对象,并复制请求消息的所有头信息。对于大多数 Spring Integration 的 MessageHandler 实现来说,当交互基于 POJO 方法调用时,这也是一样的工作方式。

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