跳到主要内容
版本:7.0.2

Service Activator

DeepSeek V3 中英对照 Service Activator

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

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

配置服务激活器

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

public class SomeService {

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

}

更多信息请参阅注解支持

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

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

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

在使用 XML 配置时,要创建服务激活器,请使用带有 input-channelref 属性的 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 属性和内部处理器定义是不允许的,因为这会造成歧义条件并导致抛出异常。

important

如果 ref 属性引用的 bean 继承自 AbstractMessageProducingHandler(例如框架自身提供的处理器),配置将通过直接将输出通道注入处理器来进行优化。在这种情况下,每个 ref 必须指向一个独立的 bean 实例(或一个 prototype 作用域的 bean),或者使用内部的 <bean/> 配置类型。如果不小心从多个 bean 引用了同一个消息处理器,将会引发配置异常。

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

自 Spring Integration 2.0 起,服务激活器也能受益于 SpEL

例如,您可以在不通过 ref 属性指向 Bean 或将其作为内部 Bean 定义包含的情况下,直接调用任何 Bean 的方法,如下所示:

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

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

在上述配置中,我们并未通过 ref 或内部 bean 的方式注入 'accountService',而是使用了 SpEL 的 @beanId 表示法,并调用了一个与消息负载类型兼容的方法。同时,我们还传递了一个头部值。任何有效的 SpEL 表达式都可以针对消息中的任何内容进行评估。对于简单的场景,如果所有逻辑都能封装在这样的表达式中,那么你的服务激活器就无需引用 bean,如下例所示:

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

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

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

异步服务激活器

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

如果服务以 Exception 完成 future,将进行常规错误处理。若存在 errorChannel 消息头,则会将 ErrorMessage 发送至该通道;否则,会将 ErrorMessage 发送至默认的 errorChannel(如果可用)。

从版本 6.1 开始,如果 AbstractMessageProducingHandler 的输出通道被配置为 ReactiveStreamsSubscribableChannel,则默认开启异步模式。如果处理器的结果不是响应式类型或 CompletableFuture<?>,那么无论输出通道类型如何,都会执行常规的回复生成流程。

另请参阅 Reactive Streams 支持 了解更多信息。

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

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

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