服务激活器
服务激活器是一种端点类型,用于将任何 Spring 管理的对象连接到输入通道,使其能够充当服务。
如果服务产生输出,它也可以连接到输出通道。
或者,产生输出的服务可以位于处理管道或消息流的末端,在这种情况下,可以使用入站消息的 replyChannel
头。
如果没有定义输出通道,这是默认行为。
与此处描述的大多数配置选项一样,相同的行为实际上适用于大多数其他组件。
服务激活器本质上是一个通用端点,用于使用输入消息(有效载荷和头)调用某个对象上的方法。
其内部逻辑基于 MessageHandler
,MessageHandler
可以是特定用例的任何可能实现,例如 DefaultMessageSplitter
、AggregatingMessageHandler
、SftpMessageHandler
、JpaOutboundGateway
等。
因此,本参考手册中提到的任何出站网关和出站通道适配器都应被视为此服务激活器端点的特定扩展;它们最终都调用某个对象的方法。
配置服务激活器
使用 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 版本开始,框架正确地将消息属性(payload
和 headers
)转换为 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>
在相同的 |
如果 ref
属性引用扩展 AbstractMessageProducingHandler
的 bean(例如框架本身提供的处理程序),则通过将输出通道直接注入处理程序来优化配置。
在这种情况下,每个 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)
)。
如果服务在 async
属性设置为 true
时返回 CompletableFuture<?>
,则调用线程会立即释放,并且回复消息会在(服务内部的)完成 future 的线程上发送。
这对于使用 PollableChannel
的长时间运行的服务特别有利,因为轮询器线程被释放以执行框架内的其他服务。
如果服务以 Exception
完成 future,则会发生正常的错误处理。
ErrorMessage
会发送到 errorChannel
消息头(如果存在)。
否则,ErrorMessage
会发送到默认的 errorChannel
(如果可用)。
从 6.1 版本开始,如果 AbstractMessageProducingHandler
的输出通道配置为 ReactiveStreamsSubscribableChannel
,则异步模式默认开启。
如果处理程序结果不是反应式类型或 CompletableFuture<?>
,则无论输出通道类型如何,都会发生常规的回复生成过程。
另请参阅 反应式流支持 以获取更多信息。
服务激活器和方法返回类型
服务方法可以返回任何类型,该类型将成为回复消息的有效载荷。
在这种情况下,会创建一个新的 Message<?>
对象,并复制请求消息中的所有头。
对于大多数 Spring Integration MessageHandler
实现,当交互基于 POJO 方法调用时,这以相同的方式工作。
也可以从方法返回一个完整的 Message<?>
对象。
但是,请记住,与 转换器 不同,对于服务激活器,此消息将通过复制请求消息中的头(如果它们尚未存在于返回的消息中)进行修改。
因此,如果您的方法参数是 Message<?>
并且您在服务方法中复制了一些(但不是全部)现有头,它们将重新出现在回复消息中。
服务激活器没有责任从回复消息中删除头,并且为了遵循松耦合原则,最好在集成流中添加一个 HeaderFilter
。
或者,可以使用转换器而不是服务激活器,但在这种情况下,当返回完整的 Message<?>
时,该方法完全负责该消息,包括复制请求消息头(如果需要)。
您必须确保重要的框架头(例如 replyChannel
、errorChannel
)(如果存在)必须保留。