|
如需使用最新稳定版本,请使用 Spring Integration 7.0.4! |
服务激活器
服务激活器是一种端点类型,用于将任何由 Spring 管理的对象连接到输入通道,使其能够扮演服务的角色。
如果服务产生输出,它也可以连接到输出通道。
或者,一个产生输出的服务可以位于处理管道或消息流的末尾,在这种情况下,可以使用传入消息的 replyChannel 标头。
如果没有定义输出通道,这是默认行为。
与这里描述的许多配置选项一样,同样的行为实际上也适用于大多数其他组件。
服务激活器本质上是一个通用端点,用于使用输入消息(有效载荷和标头)调用某个对象上的方法。
其内部逻辑基于 MessageHandler,它可以是针对特定用例的任何可能实现,例如 DefaultMessageSplitter、AggregatingMessageHandler、SftpMessageHandler、JpaOutboundGateway 等。
因此,本参考手册中提到的任何出站网关和出站通道适配器都应被视为此服务激活器端点的特定扩展;它们最终都会调用某个对象的方法。
配置服务激活器
使用 Java 和注解配置时,只需将相应的服务方法标记为 @ServiceActivator 注解——当从输入通道消费消息时,框架会自动调用该方法:
public class SomeService {
@ServiceActivator(inputChannel = "exampleChannel")
public void exampleHandler(SomeData payload) {
...
}
}
在 注解支持 中查看更多详细信息。
对于 Java、Groovy 或 Kotlin DSL,.handle() 操作符的 IntegrationFlow 表示服务激活器:
-
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注解 -
is
public -
如果
requiresReply == true,则不返回void
运行时调用的目标方法会根据每个请求消息的 payload 类型进行选择;如果目标类中存在该方法,则作为回退机制使用 Message<?> 类型。
从版本 5.0 开始,可以使用 @org.springframework.integration.annotation.Default 标记一个服务方法,作为所有不匹配情况的后备处理。
当使用 内容类型转换 并在转换后调用目标方法时,此功能非常有用。
要委托给任何对象的显式定义方法,您可以添加 method 属性,如下示例所示:
<int:service-activator input-channel="exampleChannel" ref="somePojo" method="someMethod"/>
在任一情况下,当服务方法返回非 null 值时,端点会尝试将回复消息发送到合适的回复通道。
为了确定回复通道,它首先检查是否在端点配置中提供了 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>
在同一个 <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"/>
在之前的配置中,我们不是使用 ref 注入 'accountService' 或将其作为内部 bean,而是使用 SpEL 的 @beanId 表示法,并调用一个接受与消息负载兼容类型的方法。
我们还传递了一个 header 值。
任何有效的 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 完成未来任务,则执行正常的错误处理。
如果存在,ErrorMessage 将被发送到 errorChannel 消息头。
否则,ErrorMessage 将被发送到默认的 errorChannel(如果可用)。
从 6.1 版本开始,如果 AbstractMessageProducingHandler 的输出通道被配置为 ReactiveStreamsSubscribableChannel,则默认启用异步模式。
如果处理器结果不是响应式类型或 CompletableFuture<?>,则无论输出通道类型如何,都会执行常规的消息回复处理过程。
另请参阅 响应式流支持 以获取更多信息。
服务激活器与方法返回类型
服务方法可以返回任何类型,该类型将作为响应消息的有效负载。
在此情况下,会创建一个新的 Message<?> 对象,并将请求消息中的所有头信息复制过来。
对于大多数基于 POJO 方法调用的 Spring Integration MessageHandler 实现,其行为方式相同。
方法也可以返回一个完整的 Message<?> 对象。然而,请记住,与 转换器 不同,对于服务激活器(Service Activator),如果返回的消息中尚未包含请求消息的标头,则将通过复制这些标头来修改该消息。因此,如果您的方法参数是 Message<?>,并且在服务方法中复制了部分(而非全部)现有请求头,它们将重新出现在回复消息中。将响应消息中的头信息移除并非服务激活器的职责,遵循松耦合原则,在集成流程中添加一个HeaderFilter更为合适。或者,可以使用 Transformer 替代 Service Activator。但在这种情况下,当方法返回完整的 Message<?> 时,该方法需完全负责消息的处理,包括复制请求消息头(如需要)。您必须确保重要的框架标头(例如。g.replyChannel, errorChannel), 如果存在,必须予以保留。