Spring Integration (集成)消息传递终结点_触发器

消息端点

本章的第一部分介绍了一些背景理论,并揭示了驱动Spring Integration各种消息传递组件的底层API的很多内容。 如果您想真正了解幕后发生的事情,这些信息可能会有所帮助。 但是,如果要启动并运行各种元素的简化的基于命名空间的配置,请立即跳到终结点命名空间支持。

如概述中所述,消息端点负责将各种消息传递组件连接到通道。 在接下来的几章中,我们将介绍使用消息的许多不同的组件。 其中一些还能够发送回复消息。 发送消息非常简单。 如前面的消息通道所示,您可以将消息发送到消息通道。 但是,接收有点复杂。 主要原因是有两种类型的消费者:轮询消费者和事件驱动消费者。

在这两者中,事件驱动的消费者要简单得多。 无需管理和调度单独的轮询器线程,它们本质上是具有回调方法的侦听器。 当连接到Spring Integration的可订阅消息通道之一时,这个简单的选项效果很好。 但是,当连接到缓冲、可轮询的消息通道时,某些组件必须调度和管理轮询线程。 Spring 集成提供了两种不同的端点实现来适应这两种类型的消费者。 因此,使用者自己只需要实现回调接口。 当需要轮询时,终端节点充当使用者实例的容器。 好处类似于使用容器来托管消息驱动的 bean,但是,由于这些消费者是在 Spring 管理的对象中运行,因此它更接近于 Spring 自己的容器。​​ApplicationContext​​​​MessageListener​

消息处理程序

Spring Integration的接口由框架中的许多组件实现。 换句话说,这不是公共 API 的一部分,您通常不会直接实现。 尽管如此,消息使用者使用它来实际处理使用的消息,因此了解此策略接口确实有助于理解使用者的整体角色。 接口定义如下:​​MessageHandler​​​​MessageHandler​

public interface MessageHandler {

void handleMessage(Message<?> message);

}

尽管它很简单,但此接口为以下章节中介绍的大多数组件(路由器、转换器、拆分器、聚合器、服务激活器等)提供了基础。 这些组件各自对它们处理的消息执行非常不同的功能,但实际接收消息的要求是相同的,轮询和事件驱动行为之间的选择也是相同的。 Spring 集成提供了两个端点实现,它们托管这些基于回调的处理程序,并让它们连接到消息通道。

事件驱动的消费者

因为它是两者中更简单的,所以我们首先介绍事件驱动的使用者终结点。 您可能还记得接口提供了 amethod,并且该方法接受参数(如订阅通道所示)。 下面的清单显示了方法的定义:​​SubscribableChannel​​​​subscribe()​​​​MessageHandler​​​​subscribe​

subscribableChannel.subscribe(messageHandler);

由于订阅通道的处理程序不必主动轮询该通道,因此这是一个事件驱动的消费者,Spring 集成提供的实现接受 aand a,如以下示例所示:​​SubscribableChannel​​​​MessageHandler​

SubscribableChannel channel = context.getBean("subscribableChannel", SubscribableChannel.class);

EventDrivenConsumer consumer = new EventDrivenConsumer(channel, exampleHandler);

轮询消费者

Spring Integration 也提供了一个,它可以以相同的方式实例化,除了通道必须实现,如以下示例所示:​​PollingConsumer​​​​PollableChannel​

PollableChannel channel = context.getBean("pollableChannel", PollableChannel.class);

PollingConsumer consumer = new PollingConsumer(channel, exampleHandler);

有关轮询使用者的详细信息,请参阅轮询器和通道适配器。

轮询使用者还有许多其他配置选项。 例如,触发器是必需属性。 以下示例演示如何设置触发器:

PollingConsumer consumer = new PollingConsumer(channel, handler);

consumer.setTrigger(new PeriodicTrigger(Duration.ofSeconds(30)));

Theis 通常用一个简单的区间 () 定义,但也支持 anproperty 和 booleanproperty(默认值为 — 即没有固定延迟)。 下面的示例设置这两个属性:​​PeriodicTrigger​​​​Duration​​​​initialDelay​​​​fixedRate​​​​false​

PeriodicTrigger trigger = new PeriodicTrigger(Duration.ofSeconds(1));
trigger.setInitialDelay(Duration.ofSeconds(5));
trigger.setFixedRate(true);

前面示例中的三个设置的结果是一个触发器,该触发器等待五秒钟,然后每秒触发一次。

需要一个有效的 cron 表达式。 有关详细信息,请参阅Javadoc。 以下示例设置一个新的:​​CronTrigger​​​​CronTrigger​

CronTrigger trigger = new CronTrigger("*/10 * * * * MON-FRI");

上一示例中定义的触发器的结果是每十秒触发一次的触发器,从星期一到星期五。

除了触发器之外,还可以指定其他两个与轮询相关的配置属性:and。 下面的示例演示如何设置这两个属性:​​maxMessagesPerPoll​​​​receiveTimeout​

PollingConsumer consumer = new PollingConsumer(channel, handler);

consumer.setMaxMessagesPerPoll(10);
consumer.setReceiveTimeout(5000);

该属性指定在给定轮询操作中要接收的最大消息数。 这意味着轮询器继续调用而不等待,直到返回或达到最大值。 例如,如果轮询器具有 10 秒间隔的触发和设置,并且它正在轮询队列中有 100 条消息的通道,则可以在 40 秒内检索所有 100 条消息。 它抓住 25 秒,等待 10 秒,抓住下一个 25 秒,依此类推。 配置了负值的 ifis 在单个轮询周期内调用,直到返回。 从版本 5.5 开始,avalue 具有特殊含义 - 完全跳过调用,这可以被视为暂停此轮询端点,直到稍后更改为 n 个非零值,例如通过控制总线。​​maxMessagesPerPoll​​​​receive()​​​​null​​​​maxMessagesPerPoll​​​​25​​​​maxMessagesPerPoll​​​​MessageSource.receive()​​​​null​​​​0​​​​MessageSource.receive()​​​​maxMessagesPerPoll​

该属性指定轮询器在调用接收操作时如果没有可用消息时应等待的时间量。 例如,考虑两个表面上看起来相似但实际上完全不同的选项:第一个选项的间隔触发器为 5 秒,接收超时为 50 毫秒,而第二个选项的间隔触发器为 50 毫秒,接收超时为 5 秒。 第一个消息可能比到达通道晚 4950 毫秒(如果该消息在其轮询调用返回后立即到达)。 另一方面,第二个配置永远不会错过超过 50 毫秒的消息。 不同之处在于第二个选项需要一个线程来等待。 但是,因此,它可以更快地响应到达的消息。 这种技术称为“长轮询”,可用于模拟轮询源上的事件驱动行为。​​receiveTimeout​

轮询使用者也可以委托给 Spring,如以下示例所示:​​TaskExecutor​

PollingConsumer consumer = new PollingConsumer(channel, handler);

TaskExecutor taskExecutor = context.getBean("exampleExecutor", TaskExecutor.class);
consumer.setTaskExecutor(taskExecutor);

此外,a有一个属性调用。 此属性允许您指定 aof AOP 建议,以处理其他横切关注点(包括事务)。 这些建议围绕该方法应用。 有关更深入的信息,请参阅终结点命名空间支持下有关 AOP 建议链和事务支持的部分。​​PollingConsumer​​​​adviceChain​​​​List​​​​doPoll()​

前面的示例显示了依赖项查找。 但是,请记住,这些使用者通常配置为 Spring Bean 定义。 事实上,Spring Integration还提供了acalled,根据渠道的类型创建适当的消费者类型。 此外,Spring Integration 具有完整的 XML 命名空间支持,可以进一步隐藏这些细节。 本指南介绍了基于命名空间的配置,介绍了每种组件类型。​​FactoryBean​​​​ConsumerEndpointFactoryBean​

许多实现可以生成回复消息。 如前所述,与接收消息相比,发送消息是微不足道的。 但是,发送回复消息的时间和数量取决于处理程序类型。 例如,聚合器等待大量消息到达,并且通常配置为拆分器的下游使用者,拆分器可以为它处理的每条消息生成多个回复。 使用命名空间配置时,您不需要严格知道所有详细信息。 但是,仍然值得知道的是,其中几个组件共享一个共同的基类,并且它提供了方法。​​MessageHandler​​​​AbstractReplyProducingMessageHandler​​​​setOutputChannel(..)​

终结点命名空间支持

在本参考手册中,您可以找到端点元素(如路由器、转换器、服务激活器等)的特定配置示例。 其中大多数支持属性,许多支持属性。 解析后,这些端点元素分别生成 theor 的实例,具体取决于被引用的类型:or。 当通道可轮询时,轮询行为基于终结点元素的子元素及其属性。​​input-channel​​​​output-channel​​​​PollingConsumer​​​​EventDrivenConsumer​​​​input-channel​​​​PollableChannel​​​​SubscribableChannel​​​​poller​

以下清单列出了 的所有可用配置选项:​​poller​

<int:poller cron=""                                  
default="false"
error-channel=""
fixed-delay=""
fixed-rate=""
id=""
max-messages-per-poll=""
receive-timeout=""
ref=""
task-executor=""
time-unit="MILLISECONDS"
trigger="">
<int:advice-chain />
<int:transactional />
</int:poller>

提供使用 Cron 表达式配置轮询器的功能。 基础实现使用 . 如果设置了此属性,则无需指定以下任何属性:,,, and。​​org.springframework.scheduling.support.CronTrigger​​​​fixed-delay​​​​trigger​​​​fixed-rate​​​​ref​

通过将此属性设置为 ,可以只定义一个全局默认轮询器。 如果在应用程序上下文中定义了多个默认轮询器,则会引发异常。 连接到 a() 的任何端点或任何没有显式配置轮询器的端点都将使用全局默认轮询器。 它默认为。 自选。​​true​​​​PollableChannel​​​​PollingConsumer​​​​SourcePollingChannelAdapter​​​​false​

标识在此轮询器的调用中发生故障时将错误消息发送到的通道。 若要完全禁止异常,可以提供对 的引用。 自选。​​nullChannel​

固定延迟触发器在盖子下面使用。 如果不使用 theattribute,则指定的值以毫秒为单位表示。 如果设置了此属性,则无需指定以下任何属性:,,, and。​​PeriodicTrigger​​​​time-unit​​​​fixed-rate​​​​trigger​​​​cron​​​​ref​

固定速率触发器在盖子下面使用。 如果不使用 theattribute,则指定的值以毫秒为单位表示。 如果设置了此属性,则无需指定以下任何属性:,,, and。​​PeriodicTrigger​​​​time-unit​​​​fixed-delay​​​​trigger​​​​cron​​​​ref​

引用轮询器的基础 Bean 定义的 ID,该定义类型为。 该属性对于顶级轮询器元素是必需的,除非它是默认轮询器 ()。​​org.springframework.integration.scheduling.PollerMetadata​​​​id​​​​default="true"​

有关详细信息,请参阅配置入站通道适配器​。 如果未指定,则默认值取决于上下文。 如果使用 a,则此属性默认为。 但是,如果使用 a,则属性默认为。 自选。​​PollingConsumer​​​​-1​​​​SourcePollingChannelAdapter​​​​max-messages-per-poll​​​​1​

值在基础类上设置。 如果未指定,则默认为 1000(毫秒)。 自选。​​PollerMetadata​

Bean 引用另一个顶级轮询器。 该属性不得存在于顶级元素上。 但是,如果设置了此属性,则不必指定以下任何属性:,,, 和。​​ref​​​​poller​​​​fixed-rate​​​​trigger​​​​cron​​​​fixed-delay​

提供引用自定义任务执行程序的功能。 有关详细信息,请参阅TaskExecutor 支持。 自选。

此属性指定底层证券上的枚举值。 因此,此属性只能与理论属性结合使用。 如果与 or 引用属性结合使用,则会导致失败。 支持的最小粒度(以毫秒为单位)。 因此,唯一可用的选项是毫秒和秒。 如果未提供此值,则 anyorvalue 被解释为毫秒。 基本上,此枚举为基于秒的间隔触发值提供了便利。 对于每小时、每天和每月设置,我们建议改用触发器。​​java.util.concurrent.TimeUnit​​​​org.springframework.scheduling.support.PeriodicTrigger​​​​fixed-delay​​​​fixed-rate​​​​cron​​​​trigger​​​​PeriodicTrigger​​​​fixed-delay​​​​fixed-rate​​​​cron​

对实现接口的任何 Spring 配置的 Bean 的引用。 但是,如果设置了此属性,则不必指定以下任何属性:,,, 和。 自选。​​org.springframework.scheduling.Trigger​​​​fixed-delay​​​​fixed-rate​​​​cron​​​​ref​

允许指定额外的 AOP 建议来处理其他横切问题。 有关详细信息,请参阅事务支持。 自选。

轮询器可以成为事务性的。 有关更多信息,请参阅AOP 建议链。 自选。

例子

可以按如下方式配置具有 1 秒间隔的基于间隔的简单轮询器:

<int:transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<int:poller fixed-rate="1000"/>
</int:transformer>

作为使用属性的替代方法,您也可以使用属性。​​fixed-rate​​​​fixed-delay​

对于基于 Cron 表达式的轮询器,请改用属性,如以下示例所示:​​cron​

<int:transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<int:poller cron="*/10 * * * * MON-FRI"/>
</int:transformer>

如果输入通道为 a,则需要轮询器配置。 具体来说,如前所述,这是类的必需属性。 因此,如果省略轮询使用方终结点配置的子元素,则可能会引发异常。 如果尝试在连接到不可轮询通道的元素上配置轮询器,也可能会引发异常。​​PollableChannel​​​​trigger​​​​PollingConsumer​​​​poller​

还可以创建顶级轮询器,在这种情况下,只需要属性,如以下示例所示:​​ref​

<int:poller id="weekdayPoller" cron="*/10 * * * * MON-FRI"/>

<int:transformer input-channel="pollable"
ref="transformer"
output-channel="output">
<int:poller ref="weekdayPoller"/>
</int:transformer>

该属性仅在内部轮询器定义上是允许的。 在顶级轮询器上定义此属性会导致在应用程序上下文初始化期间引发配置异常。​​ref​

全局默认轮询器

为了进一步简化配置,您可以定义全局默认轮询器。 XML DSL 中的单个顶级轮询器组件可能将属性设置为 。 对于 Java 配置,在这种情况下必须声明带有名称的 abean。 在这种情况下,任何具有 afor 其输入通道的终结点(在同一通道中定义且没有显式配置)都使用该默认值。 以下示例显示了这样的轮询器和使用它的转换器:​​default​​​​true​​​​PollerMetadata​​​​PollerMetadata.DEFAULT_POLLER​​​​PollableChannel​​​​ApplicationContext​​​​poller​

@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setMaxMessagesPerPoll(5);
pollerMetadata.setTrigger(new PeriodicTrigger(3000));
return pollerMetadata;
}

// No 'poller' attribute because there is a default global poller
@Bean
public IntegrationFlow transformFlow(MyTransformer transformer) {
return IntegrationFlow.from(MessageChannels.queue("pollable"))
.transform(transformer) // No 'poller' attribute because there is a default global poller
.channel("output")
.get();
}
交易支持

Spring Integration 还为轮询器提供了事务支持,以便每个接收和转发操作都可以作为原子工作单元执行。 要为轮询器配置事务,请添加子元素。 以下示例显示了可用属性:​​<transactional/>​

<int:poller fixed-delay="1000">
<int:transactional transaction-manager="txManager"
propagation="REQUIRED"
isolation="REPEATABLE_READ"
timeout="10000"
read-only="false"/>
</int:poller>

有关详细信息,请参阅轮询器事务支持。

AOP 咨询链

由于 Spring 事务支持依赖于代理机制(AOP Advice)处理轮询器发起的消息流的事务行为,因此您有时必须提供额外的建议来处理与轮询器相关的其他横切行为。 为此,定义了允许您在实现接口的类中添加更多建议的元素。 以下示例演示如何定义 anfor a:​​TransactionInterceptor​​​​poller​​​​advice-chain​​​​MethodInterceptor​​​​advice-chain​​​​poller​

<int:service-activator id="advicedSa" input-channel="goodInputWithAdvice" ref="testBean"
method="good" output-channel="output">
<int:poller max-messages-per-poll="1" fixed-rate="10000">
<int:advice-chain>
<ref bean="adviceA" />
<beans:bean class="org.something.SampleAdvice" />
<ref bean="txAdvice" />
</int:advice-chain>
</int:poller>
</int:service-activator>

有关如何实现接口的更多信息,请参阅Spring 框架参考指南的 AOP 部分。 建议链也可以应用于没有任何事务配置的轮询器,从而增强轮询器启动的消息流的行为。​​MethodInterceptor​

使用建议链时,不能指定子元素。 相反,声明 abean 并将其添加到 . 有关完整的配置详细信息,请参阅轮询器事务支持​。​​<transactional/>​​​​<tx:advice/>​​​​<advice-chain/>​

任务执行器支持

轮询线程可以由 Spring 抽象的任何实例执行。 这将为一个终结点或一组终结点启用并发性。 从Spring 3.0开始,核心的Spring Framework有一个命名空间,itselement支持创建一个简单的线程池执行器。 该元素接受常见并发设置的属性,例如池大小和队列容量。 配置线程池执行程序可以对终结点在负载下的性能产生重大影响。 这些设置可用于每个终结点,因为终结点的性能是要考虑的主要因素之一(另一个主要因素是终结点订阅的通道上的预期卷)。 若要为配置了 XML 命名空间支持的轮询终结点启用并发性,请提供有关其元素的引用,然后提供以下示例中显示的一个或多个属性:​​TaskExecutor​​​​task​​​​<executor/>​​​​task-executor​​​​<poller/>​

<int:poller task-executor="pool" fixed-rate="1000"/>

<task:executor id="pool"
pool-size="5-25"
queue-capacity="20"
keep-alive="120"/>

如果未提供任务执行程序,则会在调用方的线程中调用使用者的处理程序。 请注意,调用方通常是默认的(请参阅配置任务计划程序)。 您还应该记住,该属性可以通过指定 Bean 名称来提供对 Spring 接口的任何实现的引用。 前面显示的元素是为了方便起见而提供的。​​TaskScheduler​​​​task-executor​​​​TaskExecutor​​​​executor​

如前面轮询使用者的背景部分所述,您还可以以模拟事件驱动行为的方式配置轮询使用者。 通过较长的接收超时和较短的触发器间隔,您可以确保对到达的消息做出非常及时的反应,即使在轮询的消息源上也是如此。 请注意,这仅适用于具有超时的阻塞等待调用的源。 例如,文件轮询器不会阻止。 每个调用都会立即返回,并且是否包含新文件。 因此,即使轮询器包含长整型,在这种情况下也永远不会使用该值。 另一方面,当使用 Spring 集成自己的基于队列的通道时,超时值确实有机会参与。 以下示例显示轮询使用者如何几乎即时接收消息:​​receive()​​​​receive-timeout​

<int:service-activator input-channel="someQueueChannel"
output-channel="output">
<int:poller receive-timeout="30000" fixed-rate="10"/>

</int:service-activator>

使用这种方法不会带来太多开销,因为在内部,它只不过是一个定时等待线程,它不需要像(例如)一个捶打的无限 while 循环那样多的 CPU 资源使用量。

在运行时更改轮询速率

使用 aor aattribute 配置轮询器时,默认实现使用 ainstance。 这是核心 Spring 框架的一部分。 它仅接受间隔作为构造函数参数。 因此,它不能在运行时更改。​​fixed-delay​​​​fixed-rate​​​​PeriodicTrigger​​​​PeriodicTrigger​

但是,您可以定义自己的接口实现。 您甚至可以将其用作起点。 然后,您可以为间隔(周期)添加资源库,甚至可以在触发器本身中嵌入自己的限制逻辑。 该属性用于每次调用以安排下一次投票。 要在轮询器中使用此定制触发器,请在应用程序上下文中声明定制触发器的 Bean 定义,并使用引用自定义触发器 Bean 实例的属性将依赖关系注入轮询器配置中。 现在,您可以获取对触发器 Bean 的引用,并更改轮询之间的轮询间隔。​​org.springframework.scheduling.Trigger​​​​PeriodicTrigger​​​​period​​​​nextExecutionTime​​​​trigger​

有关示例,请参阅Spring 集成示例项目。 它包含一个调用的示例,该示例使用自定义触发器,并演示在运行时更改轮询间隔的功能。dynamic-poller

此示例提供了一个自定义触发器,用于实现org.springframework.scheduling.Trigger接口。 该示例的触发器基于 Spring 的PeriodicTrigger实现。 但是,自定义触发器的字段不是最终的,并且属性具有显式 getter 和 setter,允许您在运行时动态更改轮询周期。

但请务必注意,由于 Trigger 方法是,因此对动态触发器的任何更改在下一次轮询(基于现有配置)之前都不会生效。 无法强制触发器在其当前配置的下一个执行时间之前触发。​​nextExecutionTime()​

有效负载类型转换

在本参考手册中,您还可以看到接受消息或任何任意作为输入参数的各种端点的特定配置和实现示例。 在 a 的情况下,这样的参数被映射到消息有效负载或有效负载或标头的一部分(使用 Spring 表达式语言时)。 但是,端点方法的输入参数类型有时与有效负载的类型或其部分不匹配。 在这种情况下,我们需要执行类型转换。 Spring 集成提供了一种方便的方法,可以在其自己的名为转换服务 Bean 的实例中注册类型转换器(通过使用 Spring)。 一旦使用 Spring 集成基础结构定义了第一个转换器,就会自动创建该 bean。 要注册转换器,可以实现 、 或。​​Object​​​​Object​​​​ConversionService​​​​integrationConversionService​​​​org.springframework.core.convert.converter.Converter​​​​org.springframework.core.convert.converter.GenericConverter​​​​org.springframework.core.convert.converter.ConverterFactory​

实现是最简单的,从单一类型转换为另一种类型。 对于更复杂的情况,例如转换为类层次结构,可以实现 a和可能的 a。 这些使您可以完全访问 theandtype 描述符,从而实现复杂的转换。 例如,如果你有一个抽象类,叫做你的转换目标(参数类型、通道数据类型等),你有两个具体的实现调用和,并且你希望根据输入类型转换为一个或另一个,这将是一个很好的选择。 有关更多信息,请参阅以下接口的 Javadoc:​​Converter​​​​GenericConverter​​​​ConditionalConverter​​​​from​​​​to​​​​Something​​​​Thing1​​​​Thing​​​​GenericConverter​

  • org.springframework.core.convert.converter.Converter
  • org.springframework.core.convert.converter.GenericConverter
  • org.springframework.core.convert.converter.ConverterFactory

实现转换器后,可以使用方便的命名空间支持注册它,如以下示例所示:

<int:converter ref="sampleConverter"/>

<bean id="sampleConverter" class="foo.bar.TestConverter"/>

或者,您可以使用内 Bean,如以下示例所示:

<int:converter>
<bean class="o.s.i.config.xml.ConverterParserTests$TestConverter3"/>
</int:converter>

从 Spring Integration 4.0 开始,您可以使用注释来创建前面的配置,如以下示例所示:

@Component
@IntegrationConverter
public class TestConverter implements Converter<Boolean, Number> {

public Number convert(Boolean source) {
return source ? 1 : 0;
}

}

或者,您可以使用注释,如以下示例所示:​​@Configuration​

@Configuration
@EnableIntegration
public class ContextConfiguration {

@Bean
@IntegrationConverter
public SerializingConverter serializingConverter() {
return new SerializingConverter();
}

}


在配置应用程序上下文时,Spring 框架允许您添加 abean(请参阅配置转换服务​一章)。 如果需要,此服务用于在 Bean 创建和配置期间执行适当的转换。​​conversionService​



相反,用于运行时转换。 这些用途是完全不同的。 如果在运行时用于针对数据类型通道、有效负载类型转换器等中的消息进行 Spring 集成表达式评估,则用于连接 Bean 构造函数参数和属性的转换器可能会产生意外结果。​​integrationConversionService​



但是,如果您确实想要使用 Springas Spring 集成,则可以在应用程序上下文中配置别名,如以下示例所示:​​conversionService​​​​integrationConversionService​






<alias name="conversionService" alias="integrationConversionService"/>






在这种情况下,提供的转换器可用于 Spring 集成运行时转换。​​conversionService​


内容类型转换

从版本 5.0 开始,默认情况下,方法调用机制基于基础结构。 它的实现(如 and)可以使用抽象将传入转换为目标方法参数类型。 转换可以基于消息标头。 为此,Spring Integration 提供了 ,它委托给要调用的已注册转换器列表,直到其中一个返回非空结果。 默认情况下,此转换器提供(按严格顺序):​​org.springframework.messaging.handler.invocation.InvocableHandlerMethod​​​​HandlerMethodArgumentResolver​​​​PayloadArgumentResolver​​​​MessageMethodArgumentResolver​​​​MessageConverter​​​​payload​​​​contentType​​​​ConfigurableCompositeMessageConverter​

  1. MappingJackson2MessageConverter(如果类路径上存在 Jackson 处理器)
  2. ByteArrayMessageConverter
  3. ObjectStringMessageConverter
  4. GenericMessageConverter

请参阅 Javadoc(在前面的列表中链接)以获取有关其用途和转换的适当值的更多信息。 之所以使用,是因为它可以与任何其他实现一起提供,包括或排除前面提到的默认转换器。 还可以在应用程序上下文中将其注册为适当的 Bean,覆盖缺省转换器,如以下示例所示:​​contentType​​​​ConfigurableCompositeMessageConverter​​​​MessageConverter​

@Bean(name = IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME)
public ConfigurableCompositeMessageConverter compositeMessageConverter() {
List<MessageConverter> converters =
Arrays.asList(new MarshallingMessageConverter(jaxb2Marshaller()),
new JavaSerializationMessageConverter());
return new ConfigurableCompositeMessageConverter(converters);
}

这两个新转换器在默认值之前在复合中注册。 你也可以使用 abut 提供你自己的通过注册一个 bean 与名称,(通过设置属性)。​​ConfigurableCompositeMessageConverter​​​​MessageConverter​​​​integrationArgumentResolverMessageConverter​​​​IntegrationContextUtils.ARGUMENT_RESOLVER_MESSAGE_CONVERTER_BEAN_NAME​

使用 SpEL 方法调用时,基于(包括标头)的转换不可用。 在这种情况下,只有上面有效负载类型​转换中提到的常规类到类转换可用。​​MessageConverter​​​​contentType​

异步轮询

如果您希望轮询是异步的,轮询器可以选择指定指向 anybean 现有实例的属性(Spring 3.0 通过命名空间提供了方便的命名空间配置)。 但是,在使用 配置轮询器时,您必须了解某些事项。​​task-executor​​​​TaskExecutor​​​​task​​​​TaskExecutor​

问题是有两种配置,轮询器和。 它们必须彼此协调。 否则,您最终可能会造成人为的内存泄漏。​​TaskExecutor​

请考虑以下配置:

<int:channel id="publishChannel">
<int:queue />
</int:channel>

<int:service-activator input-channel="publishChannel" ref="myService">
<int:poller receive-timeout="5000" task-executor="taskExecutor" fixed-rate="50" />
</int:service-activator>

<task:executor id="taskExecutor" pool-size="20" />

上述配置演示了跑调的配置。

默认情况下,任务执行器具有无限的任务队列。 即使所有线程都被阻止,轮询器也会继续计划新任务,等待新消息到达或超时过期。 假设有 20 个线程以 5 秒超时执行任务,则它们以每秒 4 个的速率执行。 但是,新任务以每秒 20 个的速度调度,因此任务执行器中的内部队列以每秒 16 个的速度增长(当进程空闲时),因此我们存在内存泄漏。

处理此问题的方法之一是设置任务执行器的属性。 即使 0 也是一个合理的值。 您还可以通过设置任务执行器的属性(例如,to)来指定如何处理无法排队的消息来管理它。 换句话说,配置时必须了解某些细节。 有关该主题的更多详细信息,请参阅Spring参考手册中的“任务执行和调度”。​​queue-capacity​​​​rejection-policy​​​​DISCARD​​​​TaskExecutor​

端点内豆

许多端点是复合 bean。 这包括所有使用者和所有轮询的入站通道适配器。 使用者(轮询或事件驱动)委派给 a。 轮询适配器通过委托给 a 来获取消息。 通常,获取对委托 Bean 的引用很有用,也许可以在运行时更改配置或进行测试。 这些 bean 可以从具有已知名称的 bean 中获取。实例使用类似于 bean ID 注册(其中“consumer”是端点属性的值)。实例注册的 bean ID 类似于,其中 'somePolledAdapter' 是适配器的 ID。​​MessageHandler​​​​MessageSource​​​​ApplicationContext​​​​MessageHandler​​​​someConsumer.handler​​​​id​​​​MessageSource​​​​somePolledAdapter.source​

上述内容仅适用于框架组件本身。 您可以改用内部 Bean 定义,如以下示例所示:

<int:service-activator id="exampleServiceActivator" input-channel="inChannel"
output-channel = "outChannel" method="foo">
<beans:bean class="org.foo.ExampleServiceActivator"/>
</int:service-activator>

Bean 被视为与声明的任何内部 Bean 一样,并且不会在应用程序上下文中注册。 如果您希望以其他方式访问此 bean,请使用 anand 在顶层声明它,并改用属性。 有关更多信息,请参阅Spring 文档。​​id​​​​ref​

终结点角色

从版本 4.2 开始,可以将终结点分配给角色。 角色允许终结点作为一个组启动和停止。 这在使用领导选举时特别有用,在使用领导选举中,可以分别在授予或撤销领导权时启动或停止一组端点。 为此,框架在应用程序上下文中注册 abean 的名称。 每当需要控制生命周期时,都可以注入此 bean,或者:​​SmartLifecycleRoleController​​​​IntegrationContextUtils.INTEGRATION_LIFECYCLE_ROLE_CONTROLLER​​​​@Autowired​

<bean class="com.some.project.SomeLifecycleControl">
<property name="roleController" ref="integrationLifecycleRoleController"/>
</bean>

您可以使用 XML、Java 配置或以编程方式将端点分配给角色。 以下示例演示如何使用 XML 配置终结点角色:

<int:inbound-channel-adapter id="ica" channel="someChannel" expression="'foo'" role="cluster"
auto-startup="false">
<int:poller fixed-rate="60000" />
</int:inbound-channel-adapter>

以下示例演示如何为在 Java 中创建的 Bean 配置端点角色:

@Bean
@ServiceActivator(inputChannel = "sendAsyncChannel", autoStartup="false")
@Role("cluster")
public MessageHandler sendAsyncHandler() {
return // some MessageHandler
}

以下示例演示如何在 Java 中对方法配置终结点角色:

@Payload("#args[0].toLowerCase()")
@Role("cluster")
public String handle(String payload) {
return payload.toUpperCase();
}

以下示例演示如何使用 Java 中配置终结点角色:​​SmartLifecycleRoleController​

@Autowired
private SmartLifecycleRoleController roleController;
...
this.roleController.addSmartLifeCycleToRole("cluster", someEndpoint);
...

以下示例演示如何使用 anin Java 配置终结点角色:​​IntegrationFlow​

IntegrationFlow flow -> flow
.handle(..., e -> e.role("cluster"));

其中每个都向角色添加终结点。​​cluster​

调用和相应的方法启动和停止端点。​​roleController.startLifecyclesInRole("cluster")​​​​stop…​

任何实现扫描的对象都是以编程方式添加的,而不仅仅是端点。​​SmartLifecycle​

该 实现和 它自动启动和停止其配置对象 当领导权被授予或撤销时(当某些 bean 发布者时,分别)。​​SmartLifecycleRoleController​​​​ApplicationListener<AbstractLeaderEvent>​​​​SmartLifecycle​​​​OnGrantedEvent​​​​OnRevokedEvent​

使用领导选举来启动和停止组件时,必须设置 XML 属性(Bean 属性),以便应用程序上下文在上下文初始化期间不会启动组件。​​auto-startup​​​​autoStartup​​​​false​

从版本 4.3.8 开始,提供了几种状态方法:​​SmartLifecycleRoleController​

public Collection<String> getRoles() 

public boolean allEndpointsRunning(String role)

public boolean noEndpointsRunning(String role)

public Map<String, Boolean> getEndpointsRunningStatus(String role)

返回所管理角色的列表。

返回角色中的所有终结点是否正在运行。​​true​

如果角色中的任何终结点均未运行,则返回。​​true​

返回的映射。 组件名称通常是 Bean 名称。​​component name : running status​

领导力活动处理

可以分别根据授予或撤销的领导权来启动和停止终结点组。 这在共享资源必须仅由单个实例使用的群集方案中非常有用。 这方面的一个例子是轮询共享目录的文件入站通道适配器。 (请参阅读取文件)。

若要参与领导者选举并在当选领导者、撤销领导权或未能获得成为领导者的资源时收到通知,应用程序会在应用程序上下文中创建一个名为“领导者发起人”的组件。 通常,领导者发起者是 a,因此它在上下文启动时启动(可选),然后在领导更改时发布通知。 您还可以通过设置 theto(从版本 5.0 开始)来接收失败通知,以便在发生故障时执行特定操作。 按照惯例,您应该提供接收回调的 a。 您还可以通过框架提供的 aobject 撤销领导权。 您的代码还可以侦听实例(超类 ofand)并相应地做出响应(例如,通过使用 a)。 这些事件包含对对象的引用。 以下清单显示了接口的定义:​​SmartLifecycle​​​​publishFailedEvents​​​​true​​​​Candidate​​​​Context​​​​o.s.i.leader.event.AbstractLeaderEvent​​​​OnGrantedEvent​​​​OnRevokedEvent​​​​SmartLifecycleRoleController​​​​Context​​​​Context​

public interface Context {

boolean isLeader();

void yield();

String getRole();

}

从版本 5.0.6 开始,上下文提供对候选人角色的引用。

Spring 集成提供了基于抽象的领导者启动器的基本实现。 要使用它,您需要创建一个实例作为 bean,如以下示例所示:​​LockRegistry​

@Bean
public LockRegistryLeaderInitiator leaderInitiator(LockRegistry locks) {
return new LockRegistryLeaderInitiator(locks);
}

如果锁注册表正确实现,则最多只有一个领导者。 如果锁注册表还提供了在过期或中断时引发异常(理想情况下)的锁,则无领导期的持续时间可以尽可能短,具体取决于锁实现中的固有延迟所允许的。 默认情况下,该属性会增加一些额外的延迟,以防止在(更常见的)锁不完美的情况下出现 CPU 匮乏,并且只有在您尝试再次获取锁时才知道它们已过期。​​InterruptedException​​​​busyWaitMillis​

请参阅 Zookeeper 领导事件处理 有关领导选举和使用 Zookeeper的事件的更多信息。 有关使用 Hazelcast 的领导选举和事件的更多信息,请参阅Hazelcast 领导力事件处理。

消息网关

网关隐藏了 Spring 集成提供的消息传递 API。 它让你的应用程序的业务逻辑不知道 Spring 集成 API。 通过使用通用网关,代码仅与简单接互。

输入​​GatewayProxyFactoryBean​

如前所述,不依赖于 Spring Integration API 会很棒——包括网关类。 出于这个原因,Spring 集成提供了,它为任何接口生成一个代理,并在内部调用如下所示的网关方法。 通过使用依赖项注入,您可以向业务方法公开接口。​​GatewayProxyFactoryBean​

以下示例显示了一个可用于与 Spring 集成交互的接口:

package org.cafeteria;

public interface Cafe {

void placeOrder(Order order);

}

网关 XML 命名空间支持

还提供了命名空间支持。 它允许您将接口配置为服务,如以下示例所示:

<int:gateway id="cafeService"
service-interface="org.cafeteria.Cafe"
default-request-channel="requestChannel"
default-reply-timeout="10000"
default-reply-channel="replyChannel"/>

定义此配置后,现在可以将 bean 注入到其他 bean 中,并且在该接口的代理实例上调用方法的代码不知道 Spring Integration API。 请参阅“示例”附录以获取使用元素的示例(在咖啡馆演示中)。​​cafeService​​​​Cafe​​​​gateway​

上述配置中的默认值将应用于网关接口上的所有方法。 如果未指定回复超时,则调用线程将无限期地等待回复。 请参阅无响应到达时的网关行为。

可以覆盖单个方法的默认值。 请参阅使用注释和 XML 的网关配置。

设置默认回复通道

通常,您无需指定,因为网关会自动创建一个临时的匿名回复通道,用于侦听回复。 但是,在某些情况下可能会提示您定义 a(或使用适配器网关,例如 HTTP、JMS 等)。​​default-reply-channel​​​​default-reply-channel​​​​reply-channel​

对于一些背景,我们简要讨论了网关的一些内部工作原理。 网关创建临时点对点回复通道。 它是匿名的,并带有名称添加到邮件头中,。 提供显式(使用远程适配器网关)时,可以指向发布-订阅通道,之所以如此命名,是因为可以向其添加多个订阅者。 在内部,Spring 集成在临时和显式定义之间架起了一座桥梁。​​replyChannel​​​​default-reply-channel​​​​reply-channel​​​​replyChannel​​​​default-reply-channel​

假设您希望您的回复不仅发送到网关,还发送到其他一些使用者。 在这种情况下,您需要两件事:

  • 您可以订阅的命名频道
  • 该频道将成为发布-订阅-频道

网关使用的默认策略不能满足这些需求,因为添加到标头的回复通道是匿名的和点对点的。 这意味着没有其他订阅者可以获取它的句柄,即使可以,通道也具有点对点行为,因此只有一个订阅者会收到消息。 通过定义a,您可以指向您选择的频道。 在这种情况下,这是一个。 网关创建从它到存储在标头中的临时匿名回复通道的桥梁。​​default-reply-channel​​​​publish-subscribe-channel​

您可能还希望显式提供一个回复通道,用于通过侦听器(例如,窃听)进行监视或审核。 要配置通道拦截器,您需要一个命名通道。

从版本 5.4 开始,当网关方法返回类型为 时,如果未显式提供此类标头,则框架会将 aheader 填充为 abean 引用。 这允许丢弃来自下游流的任何可能的回复,从而满足单向网关协定。​​void​​​​replyChannel​​​​nullChannel​

带有注释和 XML 的网关配置

请考虑以下示例,该示例通过添加注释来扩展上一个接口示例:​​Cafe​​​​@Gateway​

public interface Cafe {

@Gateway(requestChannel="orders")
void placeOrder(Order order);

}

注释允许您添加解释为消息头的值,如以下示例所示:​​@Header​

public interface FileWriter {

@Gateway(requestChannel="filesOut")
void write(byte[] content, @Header(FileHeaders.FILENAME) String filename);

}

如果更喜欢使用 XML 方法来配置网关方法,则可以向网关配置添加元素,如以下示例所示:​​method​

<int:gateway id="myGateway" service-interface="org.foo.bar.TestGateway"
default-request-channel="inputC">
<int:default-header name="calledMethod" expression="#gatewayMethod.name"/>
<int:method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
<int:method name="echoUpperCase" request-channel="inputB"/>
<int:method name="echoViaDefault"/>
</int:gateway>

还可以使用 XML 为每个方法调用提供单独的标头。 如果要设置的标头本质上是静态的,并且您不希望使用注释将它们嵌入到网关的方法签名中,则这可能很有用。 例如,在贷款经纪人示例中,我们希望根据发起的请求类型(单引号或所有引号)影响贷款报价单的聚合方式。 通过评估调用的网关方法来确定请求的类型(尽管可能)会违反关注点分离范例(该方法是 Java 工件)。 但是,在消息传递体系结构中,在消息标头中表达您的意图(元信息)是很自然的。 下面的示例演示如何为两种方法中的每一种方法添加不同的邮件头:​​@Header​

<int:gateway id="loanBrokerGateway"
service-interface="org.springframework.integration.loanbroker.LoanBrokerGateway">
<int:method name="getLoanQuote" request-channel="loanBrokerPreProcessingChannel">
<int:header name="RESPONSE_TYPE" value="BEST"/>
</int:method>
<int:method name="getAllLoanQuotes" request-channel="loanBrokerPreProcessingChannel">
<int:header name="RESPONSE_TYPE" value="ALL"/>
</int:method>
</int:gateway>

在前面的示例中,根据网关的方法为“RESPONSE_TYPE”标头设置了不同的值。

例如,如果您指定了注释以及注释,则注释值优先。​​requestChannel​​​​<int:method/>​​​​@Gateway​

如果在 XML 中指定了无参数网关,并且接口方法同时具有 aandannotation(带有 aor ain anelement),则忽略 thevalue。​​@Payload​​​​@Gateway​​​​payloadExpression​​​​payload-expression​​​​<int:method/>​​​​@Payload​

表达式和“全局”标头

元素支持作为替代方案。 计算 SpEL 表达式以确定标头的值。 从版本 5.2 开始,评估上下文的对象是 awithandaccessors。 例如,如果要路由简单方法名称,则可以使用以下表达式添加标头:​​<header/>​​​​expression​​​​value​​​​#root​​​​MethodArgsHolder​​​​getMethod()​​​​getArgs()​​​​method.name​

不可序列化。 如果以后序列化消息,则带有表达式 of 的标头将丢失。 因此,您可能希望在这些情况下使用。 该方法提供方法的表示形式,包括参数和返回类型。​​java.reflect.Method​​​​method​​​​method.name​​​​method.toString()​​​​toString()​​​​String​

从版本 3.0 开始,可以定义元素以向网关生成的所有消息添加标头,而不考虑调用的方法。 为方法定义的特定标头优先于默认标头。 此处为方法定义的特定标头将覆盖服务接口中的任意注释。 但是,默认标头不会覆盖服务接口中的任意注释。​​<default-header/>​​​​@Header​​​​@Header​

网关现在还支持 a,它适用于所有方法(除非被覆盖)。​​default-payload-expression​

将方法参数映射到消息

使用上一节中的配置技术可以控制如何将方法参数映射到消息元素(有效负载和标头)。 如果未使用显式配置,则使用某些约定来执行映射。 在某些情况下,这些约定无法确定哪个参数是有效负载,哪些参数应映射到标头。 请考虑以下示例:

public String send1(Object thing1, Map thing2);

public String send2(Map thing1, Map thing2);

在第一种情况下,约定是将第一个参数映射到有效负载(只要它不是 a),第二个参数的内容将成为标头。​​Map​

在第二种情况下(或第一种情况下,当参数为 a 时),框架无法确定哪个参数应该是有效负载。 因此,映射失败。 这通常可以使用 a、aannotation 或 aannotation 来解决。​​thing1​​​​Map​​​​payload-expression​​​​@Payload​​​​@Headers​

或者(每当约定崩溃时),您可以承担将方法调用映射到消息的全部责任。 为此,实现 anand 通过使用属性将其提供给 the。 映射器映射 a,这是一个包装实例并包含参数的简单类。 提供自定义映射器时,网关上不允许使用属性和元素。 同样,属性和元素不允许在任何元素上。​​MethodArgsMessageMapper​​​​<gateway/>​​​​mapper​​​​MethodArgsHolder​​​​java.reflect.Method​​​​Object[]​​​​default-payload-expression​​​​<default-header/>​​​​payload-expression​​​​<header/>​​​​<method/>​

映射方法参数

以下示例演示如何将方法参数映射到消息,并显示一些无效配置的示例:

public interface MyGateway {

void payloadAndHeaderMapWithoutAnnotations(String s, Map<String, Object> map);

void payloadAndHeaderMapWithAnnotations(@Payload String s, @Headers Map<String, Object> map);

void headerValuesAndPayloadWithAnnotations(@Header("k1") String x, @Payload String s, @Header("k2") String y);

void mapOnly(Map<String, Object> map); // the payload is the map and no custom headers are added

void twoMapsAndOneAnnotatedWithPayload(@Payload Map<String, Object> payload, Map<String, Object> headers);

@Payload("#args[0] + #args[1] + '!'")
void payloadAnnotationAtMethodLevel(String a, String b);

@Payload("@someBean.exclaim(#args[0])")
void payloadAnnotationAtMethodLevelUsingBeanResolver(String s);

void payloadAnnotationWithExpression(@Payload("toUpperCase()") String s);

void payloadAnnotationWithExpressionUsingBeanResolver(@Payload("@someBean.sum(#this)") String s); //

// invalid
void twoMapsWithoutAnnotations(Map<String, Object> m1, Map<String, Object> m2);

// invalid
void twoPayloads(@Payload String s1, @Payload String s2);

// invalid
void payloadAndHeaderAnnotationsOnSameParameter(@Payload @Header("x") String s);

// invalid
void payloadAndHeadersAnnotationsOnSameParameter(@Payload @Headers Map<String, Object> map);

}

请注意,在此示例中,SpEL 变量 () 引用参数 — 在本例中为 的值。​​#this​​​​s​

XML 等效项看起来略有不同,因为方法参数没有上下文。 但是,表达式可以使用变量引用方法参数,如以下示例所示:​​#this​​​​#args​

<int:gateway id="myGateway" service-interface="org.something.MyGateway">
<int:method name="send1" payload-expression="#args[0] + 'thing2'"/>
<int:method name="send2" payload-expression="@someBean.sum(#args[0])"/>
<int:method name="send3" payload-expression="#method"/>
<int:method name="send4">
<int:header name="thing1" expression="#args[2].toUpperCase()"/>
</int:method>
</int:gateway>

​@MessagingGateway​​注解

从版本 4.0 开始,可以使用 aannotation 标记网关服务接口,而不需要定义 axml 元素进行配置。 以下一对示例比较了配置同一网关的两种方法:​​@MessagingGateway​​​​<gateway />​

<int:gateway id="myGateway" service-interface="org.something.TestGateway"
default-request-channel="inputC">
<int:default-header name="calledMethod" expression="#gatewayMethod.name"/>
<int:method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
<int:method name="echoUpperCase" request-channel="inputB">
<int:header name="thing1" value="thing2"/>
</int:method>
<int:method name="echoViaDefault"/>
</int:gateway>
@MessagingGateway(name = "myGateway", defaultRequestChannel = "inputC",
defaultHeaders = @GatewayHeader(name = "calledMethod",
expression="#gatewayMethod.name"))
public interface TestGateway {

@Gateway(requestChannel = "inputA", replyTimeout = 2, requestTimeout = 200)
String echo(String payload);

@Gateway(requestChannel = "inputB", headers = @GatewayHeader(name = "thing1", value="thing2"))
String echoUpperCase(String payload);

String echoViaDefault(String payload);

}

与XML版本类似,当Spring Integration在组件扫描期间发现这些注释时,它会使用其消息传递基础结构创建实现。 要执行此扫描并在应用程序上下文中注册,请将注释添加到 aclass。 标准基础结构不处理接口。 因此,我们引入了自定义逻辑来查找接口上的注释并为它们注册实例。 另请参阅批注支持​。​​proxy​​​​BeanDefinition​​​​@IntegrationComponentScan​​​​@Configuration​​​​@ComponentScan​​​​@IntegrationComponentScan​​​​@MessagingGateway​​​​GatewayProxyFactoryBean​

除了注释之外,您还可以使用注释标记服务接口,以避免创建 Bean(如果此类配置文件未处于活动状态)。​​@MessagingGateway​​​​@Profile​

从 6.0 版开始,与 thecan 的接口也可以用相应配置逻辑的注释来标记,因为它可以在任何 Springdefinition 中使用。​​@MessagingGateway​​​​@Primary​​​​@Component​

从 6.0 版开始,可以在标准 Spring 配置中使用接口。 这可以用作 theor manualbean 定义的替代方法。​​@MessagingGateway​​​​@Import​​​​@IntegrationComponentScan​​​​AnnotationGatewayProxyFactoryBean​

Theis 元注释与 asince 版本和属性本质上是别名的。 这样,网关代理的 Bean 名称生成策略与扫描和导入组件的标准 Spring 注释配置重新对齐。 默认值可以通过 anor 作为属性进行全局覆盖。​​@MessagingGateway​​​​@MessageEndpoint​​​​6.0​​​​name()​​​​@Compnent.value()​​​​AnnotationBeanNameGenerator​​​​AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR​​​​@IntegrationComponentScan.nameGenerator()​

如果没有 XML 配置,则至少需要一个类的注释。 有关详细信息,请参阅配置和@EnableIntegration​。​​@EnableIntegration​​​​@Configuration​

调用无参数方法

在没有任何参数的网关接口上调用方法时,默认行为是从 a 接收 a。​​Message​​​​PollableChannel​

但是,有时您可能希望触发无参数方法,以便可以与不需要用户提供的参数的其他下游组件进行交互,例如触发无参数 SQL 调用或存储过程。

若要实现发送和接收语义,必须提供有效负载。 要生成有效负载,接口上的方法参数不是必需的。 您可以在元素上使用 theannotation 或 XML 中的属性。 以下列表包括有效负载可能是什么的几个示例:​​@Payload​​​​payload-expression​​​​method​

  • 文本字符串
  • #gatewayMethod.name
  • new java.util.Date()
  • @someBean.someMethod() 的返回值

以下示例演示如何使用注释:​​@Payload​

public interface Cafe {

@Payload("new java.util.Date()")
List<Order> retrieveOpenOrders();

}

您也可以使用注释。​​@Gateway​

public interface Cafe {

@Gateway(payloadExpression = "new java.util.Date()")
List<Order> retrieveOpenOrders();

}

如果两个注释都存在(并且提供了注释),则获胜。​​payloadExpression​​​​@Gateway​

另请参阅使用注释和 XML 进行网关配置。

如果方法没有参数和返回值,但包含有效负载表达式,则将其视为仅发送操作。

调用方法​​default​

网关代理的接口也可能具有方法,从版本 5.3 开始,框架将 a注入代理以使用 aapproach 而不是代理调用方法。 来自 JDK 的接口(例如)仍可用于网关代理,但由于针对 JDK 类实例化的内部 Java 安全原因,无法调用它们的方法。 这些方法也可以使用方法上的显式注释(或 theannotation 或 XML 组件)进行代理(丢失其实现逻辑,同时恢复以前的网关代理行为)。​​default​​​​DefaultMethodInvokingMethodInterceptor​​​​default​​​​java.lang.invoke.MethodHandle​​​​java.util.function.Function​​​​default​​​​MethodHandles.Lookup​​​​@Gateway​​​​proxyDefaultMethods​​​​@MessagingGateway​​​​<gateway>​

错误处理

网关调用可能会导致错误。 默认情况下,下游发生的任何错误都会在网关的方法调用时“按原样”重新引发。 例如,请考虑以下简单流:

gateway -> service-activator

如果服务激活器调用的服务抛出 a(例如),则框架将其包装在 a,并将传递给属性中的服务激活器的消息附加。 因此,框架执行的任何日志记录都具有失败的完整上下文。 默认情况下,当网关捕获异常时,解包并抛出给调用方。 您可以在网关方法声明上配置 aclause 以匹配原因链中的特定异常类型。 例如,如果要捕获一个整体与下游错误原因的所有消息传递信息,则应具有类似于以下内容的网关方法:​​MyException​​​​MessagingException​​​​failedMessage​​​​MyException​​​​throws​​​​MessagingException​

public interface MyGateway {

void performProcess() throws MessagingException;

}

由于我们鼓励 POJO 编程,因此您可能不希望将调用方暴露给消息传递基础结构。

如果您的网关方法没有 aclause,则网关将遍历原因树,查找不是 a。 如果未找到,则框架将抛出 。 如果在前面的讨论中有 和 你的方法的原因,网关会进一步解开包装并将其抛给调用方。​​throws​​​​RuntimeException​​​​MessagingException​​​​MessagingException​​​​MyException​​​​SomeOtherException​​​​throws SomeOtherException​

当网关声明为 no 时,将使用内部框架接口。​​service-interface​​​​RequestReplyExchanger​

请考虑以下示例:

public interface RequestReplyExchanger {

Message<?> exchange(Message<?> request) throws MessagingException;

}

在 5.0 版之前,此方法没有子句,因此异常被解包。 如果您使用此界面并希望恢复以前的解包行为,请使用自定义或访问自己。​​exchange​​​​throws​​​​service-interface​​​​cause​​​​MessagingException​

但是,您可能希望记录错误而不是传播错误,或者您可能希望将异常视为有效回复(通过将其映射到符合调用方理解的某些“错误消息”协定的消息)。 为此,网关通过包含对属性的支持来提供对专用于错误的消息通道的支持。 在以下示例中,“转换器”从以下位置创建回复:​​error-channel​​​​Message​​​​Exception​

<int:gateway id="sampleGateway"
default-request-channel="gatewayChannel"
service-interface="foo.bar.SimpleGateway"
error-channel="exceptionTransformationChannel"/>

<int:transformer input-channel="exceptionTransformationChannel"
ref="exceptionTransformer" method="createErrorResponse"/>

这可能是一个简单的POJO,它知道如何创建预期的错误响应对象。 这将成为发送回调用方的有效负载。 如有必要,您可以在这样的“错误流”中做更多复杂的事情。 它可能涉及路由器(包括Spring Integration),过滤器等。 然而,大多数时候,一个简单的“变压器”就足够了。​​exceptionTransformer​​​​ErrorMessageExceptionTypeRouter​

或者,您可能只想记录异常(或将其异步发送到某个位置)。 如果提供单向流,则不会将任何内容发送回调用方。 如果要完全抑制异常,可以提供对全局(本质上是方法)的引用。 最后,如上所述,如果定义了 nois,则异常会像往常一样传播。​​nullChannel​​​​/dev/null​​​​error-channel​

当你使用注释(参见)时,你可以使用一个属性。​​@MessagingGateway​​​​​@MessagingGateway​​ Annotation​​​​errorChannel​

从版本 5.0 开始,当您使用具有 areturn 类型(单向流)的网关方法时,引用(如果提供)将填充在每个已发送消息的标准标头中。 此功能允许基于标准配置(或 a)的下游异步流覆盖默认的全局异常发送行为。 以前,您必须手动指定带有注释或元素的标头。 对于具有异步流的方法,该属性被忽略。 相反,错误消息已发送到默认值。​​void​​​​error-channel​​​​errorChannel​​​​ExecutorChannel​​​​QueueChannel​​​​errorChannel​​​​errorChannel​​​​@GatewayHeader​​​​<header>​​​​error-channel​​​​void​​​​errorChannel​

通过简单的 POJI 网关公开消息传递系统是有好处的,但“隐藏”底层消息传递系统的现实确实是有代价的,因此您应该考虑某些事情。 我们希望我们的 Java 方法尽快返回,而不是在调用方等待它返回时无限期挂起(无论是 void、返回值还是抛出的异常)。 当常规方法用作消息传递系统前面的代理时,我们必须考虑底层消息传递的潜在异步性质。 这意味着由网关启动的消息可能会被筛选器丢弃,并且永远不会到达负责生成回复的组件。 某些服务激活器方法可能会导致异常,因此不提供回复(因为我们不生成空消息)。 换句话说,多种情况可能会导致回复消息永远不会出现。 这在消息传递系统中是完全自然的。 但是,请考虑对网关方法的影响。网关的方法输入参数被合并到消息中并发送到下游。 回复消息将转换为网关方法的返回值。 因此,您可能希望确保对于每个网关调用,始终有回复消息。 否则,网关方法可能永远不会返回并无限期挂起。 处理这种情况的一种方法是使用异步网关(本节稍后将介绍)。 处理它的另一种方法是显式设置属性。 这样,网关的挂起时间不会超过 the,如果超时已过,则返回“null”。 最后,您可能需要考虑在服务激活器上设置下游标志,例如“requires-reply”,或在筛选器上设置“拒绝时引发异常”。本章的最后一节将更详细地讨论这些选项。​​reply-timeout​​​​reply-timeout​

如果下游流返回 an,则将其 (a) 视为常规下游错误。 如果有配置,则会将其发送到错误流。 否则,有效负载将抛出给网关的调用方。 同样,如果错误流返回 an,则其有效负载将抛给调用方。 这同样适用于任何具有有效负载的消息。 这在需要直接传播到调用方的异步情况下非常有用。 为此,您可以返回 an(作为来自某些服务)或扔掉它。 通常,即使使用异步流,框架也会负责将下游流引发的异常传播回网关。 TCP 客户端-服务器多路复用​示例演示了将异常返回给调用方的这两种技术。 它通过使用 anwith(请参阅聚合器和组超时​)对丢弃流进行 areply 来模拟等待线程的套接字 IO 错误。​​ErrorMessage​​​​payload​​​​Throwable​​​​error-channel​​​​error-channel​​​​ErrorMessage​​​​Throwable​​​​Exception​​​​Exception​​​​reply​​​​aggregator​​​​group-timeout​​​​MessagingTimeoutException​

网关超时

网关有两个超时属性:and。 仅当通道可以阻塞(例如,有界即已满)时,请求超时才适用。 该值是网关等待回复或返回的时间。 它默认为无穷大。​​requestTimeout​​​​replyTimeout​​​​QueueChannel​​​​replyTimeout​​​​null​

可以将超时设置为网关(和)或接口注释上所有方法的默认值。 单个方法可以覆盖这些默认值(子元素)或注释。​​defaultRequestTimeout​​​​defaultReplyTimeout​​​​MessagingGateway​​​​<method/>​​​​@Gateway​

从版本 5.0 开始,可以将超时定义为表达式,如以下示例所示:

@Gateway(payloadExpression = "#args[0]", requestChannel = "someChannel",
requestTimeoutExpression = "#args[1]", replyTimeoutExpression = "#args[2]")
String lateReply(String payload, long requestTimeout, long replyTimeout);

评估上下文具有 a(用于引用其他 bean),并且数组变量可用。​​BeanResolver​​​​@someBean​​​​#args​

使用 XML 进行配置时,超时属性可以是长整型值或 SpEL 表达式,如以下示例所示:

<method name="someMethod" request-channel="someRequestChannel"
payload-expression="#args[0]"
request-timeout="1000"
reply-timeout="#args[1]">
</method>

异步网关

作为一种模式,消息传递网关提供了一种很好的方法来隐藏特定于消息传递的代码,同时仍公开消息传递系统的全部功能。 如​​前所述​​,提供了一种通过服务接口公开代理的便捷方法,使您可以基于 POJO 访问消息传递系统(基于您自己的域中的对象、原语/字符串或其他对象)。 但是,当网关通过返回值的简单 POJO 方法公开时,这意味着对于每个请求消息(在调用方法时生成),必须有一个回复消息(在方法返回时生成)。 由于消息传递系统自然是异步的,因此您可能无法始终保证“对于每个请求,总会有回复”的合同。Spring Integration 2.0引入了对异步网关的支持,当您可能不知道是否期望回复或回复到达需要多长时间时,它提供了一种启动流程的便捷方法。​​GatewayProxyFactoryBean​

为了处理这些类型的场景,Spring 集成使用实例来支持异步网关。​​java.util.concurrent.Future​

从 XML 配置来看,没有任何变化,您仍然以与定义常规网关相同的方式定义异步网关,如以下示例所示:

<int:gateway id="mathService"
service-interface="org.springframework.integration.sample.gateway.futures.MathServiceGateway"
default-request-channel="requestChannel"/>

但是,网关接口(服务接口)略有不同,如下所示:

public interface MathServiceGateway {

Future<Integer> multiplyByTwo(int i);

}

如前面的示例所示,网关方法的返回类型为 a。 当看到网关方法的返回类型为 a 时,它会立即使用 an 切换到异步模式。 这就是差异的程度。 对此类方法的调用始终立即返回 ainstance。 然后,您可以按照自己的节奏与它进行交互以获取结果,取消等。 此外,与实例的任何其他用途一样,调用可能会显示超时、执行异常等。 以下示例演示如何使用从异步网关返回的 a:​​Future​​​​GatewayProxyFactoryBean​​​​Future​​​​AsyncTaskExecutor​​​​Future​​​​Future​​​​Future​​​​get()​​​​Future​

MathServiceGateway mathService = ac.getBean("mathService", MathServiceGateway.class);
Future<Integer> result = mathService.multiplyByTwo(number);
// do something else here since the reply might take a moment
int finalResult = result.get(1000, TimeUnit.SECONDS);

有关更详细的示例,请参阅 Spring 集成示例中的异步网关示例。

​AsyncTaskExecutor​

默认情况下,theuses当为返回类型为 a 的任何网关方法提交内部实例时。 但是,元素配置中的属性允许您提供对 Spring 应用程序上下文中可用的任何实现的引用。​​GatewayProxyFactoryBean​​​​org.springframework.core.task.SimpleAsyncTaskExecutor​​​​AsyncInvocationTask​​​​Future​​​​async-executor​​​​<gateway/>​​​​java.util.concurrent.Executor​

(默认)支持两者和返回类型。 请参阅可折叠的未来。 即使存在默认执行程序,提供外部执行程序通常也很有用,以便您可以在日志中标识其线程(使用 XML 时,线程名称基于执行程序的 Bean 名称),如以下示例所示:​​SimpleAsyncTaskExecutor​​​​Future​​​​CompletableFuture​

@Bean
public AsyncTaskExecutor exec() {
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
simpleAsyncTaskExecutor.setThreadNamePrefix("exec-");
return simpleAsyncTaskExecutor;
}

@MessagingGateway(asyncExecutor = "exec")
public interface ExecGateway {

@Gateway(requestChannel = "gatewayChannel")
Future<?> doAsync(String foo);

}

如果您希望返回不同的实现,则可以提供自定义执行器或完全禁用执行器,并从下游流返回回复消息有效负载。 要禁用执行程序,请将其设置为(通过使用)。 使用 XML 配置网关时,请使用。 使用注释进行配置时,请使用类似于以下内容的代码:​​Future​​​​Future​​​​null​​​​GatewayProxyFactoryBean​​​​setAsyncTaskExecutor(null)​​​​async-executor=""​​​​@MessagingGateway​

@MessagingGateway(asyncExecutor = AnnotationConstants.NULL)
public interface NoExecGateway {

@Gateway(requestChannel = "gatewayChannel")
Future<?> doAsync(String foo);

}

如果返回类型是特定的具体实现或配置的执行程序不支持的某个其他子接口,则流在调用方的线程上运行,并且流必须在回复消息有效负载中返回所需的类型。​​Future​

​CompletableFuture​

从版本 4.2 开始,网关方法现在可以返回。 返回此类型时有两种操作模式:​​CompletableFuture<?>​

  • 当提供异步执行器并且返回类型完全(不是子类)时,框架会在执行器上运行任务,并立即返回 ato 用于创建未来的 caller.is。CompletableFutureCompletableFutureCompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor)
  • 当异步执行器显式设置为 并且返回类型是返回类型是 的子类时,将在调用方的线程上调用流。 在此方案中,下游流应返回适当的类型。nullCompletableFutureCompletableFutureCompletableFuture

从 Spring Framework 开始已被弃用。 建议现在迁移到提供类似处理功能的哪个。​​org.springframework.util.concurrent.ListenableFuture​​​​6.0​​​​CompletableFuture​

使用场景

在以下方案中,调用方线程立即返回 a,当下游流回复网关(使用 anobject)时完成。​​CompletableFuture<Invoice>​​​​Invoice​

CompletableFuture<Invoice> order(Order order);
<int:gateway service-interface="something.Service" default-request-channel="orders" />

在以下方案中,调用方线程返回 awhen 下游流将其作为对网关的回复的有效负载。 当发票准备好时,必须完成将来的其他一些过程。​​CompletableFuture<Invoice>​

CompletableFuture<Invoice> order(Order order);
<int:gateway service-interface="foo.Service" default-request-channel="orders"
async-executor="" />

在以下方案中,调用方线程返回 awhen 下游流将其作为对网关的回复的有效负载。 当发票准备好时,必须完成将来的其他一些过程。 如果启用了 iflogging,则会发出一个日志条目,指示异步执行器不能用于此方案。​​CompletableFuture<Invoice>​​​​DEBUG​

MyCompletableFuture<Invoice> order(Order order);
<int:gateway service-interface="foo.Service" default-request-channel="orders" />

​CompletableFuture​​实例可用于对回复执行其他操作,如以下示例所示:

CompletableFuture<String> process(String data);

...

CompletableFuture result = process("foo")
.thenApply(t -> t.toUpperCase());

...

String out = result.get(10, TimeUnit.SECONDS);
反应器​​Mono​

从版本 5.0 开始,允许使用Mono<T>return 类型将项目反应器与网关接口方法一起使用。 内部包裹在 a 中。​​GatewayProxyFactoryBean​​​​AsyncInvocationTask​​​​Mono.fromCallable()​

A可用于稍后检索结果(类似于a),或者您可以通过调用您的结果何时返回到网关来与调度程序一起使用。​​Mono​​​​Future<?>​​​​Consumer​

这并没有立即被框架冲洗。 因此,在网关方法返回之前不会启动底层消息流(就像 atask 一样)。 流在订阅时开始。 或者,当它们与整体相关时,(作为“可组合”)可能是反应堆流的一部分。 以下示例演示如何使用项目反应器创建网关:​​Mono​​​​Future<?>​​​​Executor​​​​Mono​​​​Mono​​​​subscribe()​​​​Flux​

@MessagingGateway
public interface TestGateway {

@Gateway(requestChannel = "multiplyChannel")
Mono<Integer> multiply(Integer value);

}

@ServiceActivator(inputChannel = "multiplyChannel")
public Integer multiply(Integer value) {
return value * 2;
}

其中这样的网关可以在处理数据的某些服务中使用:​​Flux​

@Autowired
TestGateway testGateway;

public void hadnleFlux() {
Flux.just("1", "2", "3", "4", "5")
.map(Integer::parseInt)
.flatMap(this.testGateway::multiply)
.collectList()
.subscribe(System.out::println);
}

另一个使用项目反应器的示例是一个简单的回调方案,如以下示例所示:

Mono<Invoice> mono = service.process(myOrder);

mono.subscribe(invoice -> handleInvoice(invoice));

调用线程继续,在流完成时不被调用。​​handleInvoice()​

另请参阅Kotlin 协程了解更多信息。

返回异步类型的下游流

如上面的AsyncTaskExecutor部分所述,如果您希望某个下游组件返回带有异步有效负载(、 和其他)的消息,则必须将异步执行器显式设置为(或使用 XML 配置时)。 然后,在调用方线程上调用流,稍后可以检索结果。​​Future​​​​Mono​​​​null​​​​""​

异步返回类型​​void​

消息网关方法可以像这样声明:

@MessagingGateway
public interface MyGateway {

@Gateway(requestChannel = "sendAsyncChannel")
@Async
void sendAsync(String payload);

}

但下游异常不会传播回调用方。 为了确保下游流调用和异常传播到调用方的异步行为,从版本 6.0 开始,框架提供了对 theandreturn 类型的支持。 该用例类似于之前描述的 plainreturn 类型的发送和忘记行为,但不同之处在于流执行异步发生,并且根据操作结果异常地使用 aor 完成返回(or)。​​Future<Void>​​​​Mono<Void>​​​​void​​​​Future​​​​Mono​​​​null​​​​send​

如果下游流回复精确,则必须将网关的选项设置为 null(对于配置),并且该部件在生产者线程上执行。 回复取决于下游流配置。 这样,目标应用程序就可以正确生成回复。 用例已经超出了框架线程控制,因此设置为 null 没有意义。 因此,请求-答复网关操作的结果必须配置为网关方法的返回类型。​​Future<Void>​​​​asyncExecutor​​​​AnnotationConstants.NULL​​​​@MessagingGateway​​​​send​​​​Future<Void>​​​​Mono​​​​asyncExecutor​​​​Mono<Void>​​​​Mono<?>​

没有响应到达时的网关行为

如前所述,网关提供了一种通过 POJO 方法调用与消息传递系统交互的便捷方式。 但是,通常期望始终返回(即使有 Exception)的典型方法调用可能并不总是一对一映射到消息交换(例如,回复消息可能不会到达 — 相当于方法不返回)。

本节的其余部分将介绍各种方案以及如何使网关的行为更可预测。 可以配置某些属性以使同步网关行为更可预测,但其中一些属性可能并不总是按预期工作。 其中之一是(在方法级别或网关级别)。 我们检查该属性,以了解它在各种情况下如何影响同步网关的行为。 我们检查单线程方案(下游的所有组件都通过直接通道连接)和多线程方案(例如,下游的某个地方可能有打破单线程边界的可轮询或执行器通道)。​​reply-timeout​​​​default-reply-timeout​​​​reply-timeout​

下游长时间运行的流程

同步网关,单线程

如果下游组件仍在运行(可能是因为无限循环或服务速度慢),则设置 a 不起作用,并且网关方法调用在下游服务退出(通过返回或引发异常)之前不会返回。​​reply-timeout​

同步网关,多线程

如果下游组件仍在多线程消息流中运行(可能是由于无限循环或服务缓慢),那么设置该组件的效果是允许网关方法调用在达到超时后返回,因为轮询在回复通道上,等待消息直到超时到期。 但是,如果在生成实际回复之前已达到超时,则可能导致网关方法返回“null”。 您应该了解,回复消息(如果生成)是在网关方法调用可能返回后发送到回复通道的,因此您必须意识到这一点,并在设计流时牢记这一点。​​reply-timeout​​​​GatewayProxyFactoryBean​

下游组件返回“null”

同步网关 — 单线程

如果下游组件返回“null”并且已配置 no,则网关方法调用将无限期挂起,除非已配置或已在可能返回“null”的下游组件(例如,服务激活器)上设置属性。 在这种情况下,将引发异常并将其传播到网关。​​reply-timeout​​​​reply-timeout​​​​requires-reply​

同步网关 — 多线程

该行为与前一种情况相同。

下游组件返回签名为“无效”,而网关方法签名为非无效

同步网关 — 单线程

如果组件下游返回“void”并且已配置 no,则网关方法调用将无限期挂起,除非已配置 a。​​reply-timeout​​​​reply-timeout​

同步网关 — 多线程

该行为与前一种情况相同。

下游组件导致运行时异常

同步网关 — 单线程

如果下游组件引发运行时异常,则异常将通过错误消息传播回网关并重新引发。

同步网关 — 多线程

该行为与前一种情况相同。

您应该了解,默认情况下,它是无限的。 因此,如果未显式设置 ,网关方法调用可能会无限期挂起。 因此,为了确保分析流,并且如果发生这些场景之一的可能性很小,则应将该属性设置为“安全”值。 更好的是,您可以将下游组件的属性设置为“true”以确保及时响应,因为一旦下游组件在内部返回 null,就会引发异常。 但是,您还应该意识到,有些情况(请参阅第一个​)无济于事。 这意味着分析消息流并决定何时使用同步网关而不是异步网关也很重要。 如前所述​,后一种情况是定义返回实例的网关方法的问题。 然后,您可以保证收到该返回值,并且可以更精细地控制调用结果。 此外,在处理路由器时,您应该记住,如果路由器无法解析特定通道,将属性设置为“true”会导致路由器引发异常。 同样,在处理过滤器时,您可以设置属性。 在这两种情况下,生成的流的行为就像它包含具有“requires-reply”属性的服务激活器一样。 换句话说,它有助于确保网关方法调用的及时响应。​​reply-timeout​​​​reply-timeout​​​​reply-timeout​​​​requires-reply​​​​reply-timeout​​​​Future​​​​resolution-required​​​​throw-exception-on-rejection​

​reply-timeout​​​是元素的无界(由 创建)。 用于外部集成的入站网关(WS、HTTP 等)与这些网关共享许多特征和属性。 但是,对于这些入站网关,默认值为 1000 毫秒(一秒)。 如果对另一个线程进行下游异步切换,则可能需要增加此属性,以便在网关超时之前有足够的时间完成流。​​<gateway/>​​​​GatewayProxyFactoryBean​​​​reply-timeout​

您应该了解,计时器在线程返回到网关时启动,即在流完成或消息传递给另一个线程时启动。 此时,调用线程开始等待回复。 如果流是完全同步的,则回复立即可用。 对于异步流,线程会等待到这个时间。

请参阅 Java DSL 一章中的IntegrationFlowas Gateway,了解定义网关的选项。​​IntegrationFlow​