什么是Spring Integration
Spring Integration是一个功能强大的EIP (Enterprise Integration Patterns),即企业集成模式。它是Spring Messaging的扩展,提供了Spring编程模型的扩展,用来支持企业集成模式。它集成了众多功能,是一种便捷的事件驱动消息框架用来在系统之间做消息传递的。
Spring Integration的主要特征和功能如下:
-
消息驱动架构:使用消息作为信息的载体,通过订阅和发布消息实现系统之间的通信和集成。
-
使用通道与过滤器:通道负责传输消息,过滤器对消息进行操作与路由。通过将它们组合可以构建完整的消息流和处理流程。
-
支持多种通信范式:如点对点、发布订阅等。也支持异步和流式处理。
-
松耦合和可扩展性好:通过消息传递降低系统耦合度。提供通用接口支持不同传输技术的适配。
-
与Spring架构无缝集成:可以使用Spring资源及事务管理功能。开发更简单规范。
-
丰富的组件:内置通道组件支持文件、JMS、HTTP、Email等。还可以自定义组件。
-
支持DSL:声明式配置简化集成开发。
Spring Integration的基本结构
Message
在Spring Integration中,消息是任何Java对象的通用包装器,与框架一起处理该对象时使用元数据组合。它由有效负载(Payload)和标头(Header)组成。按照我个人的理解,有效负载就是我们实际要传输的数据或者信息,而标头则是随之携带而来的一些附加信息,比如ip地址、hostname等信息。
有效负载可以是任何类型,标头包含常用信息,例如ID、时间戳、相关ID和返回地址。
标头还用于在连接的传输之间传递值。例如,从接收到的文件创建消息时,文件名可以存储在标头中以便下游组件访问。同样,如果消息的内容最终将由出站邮件适配器发送,则上游组件可以将各种属性(收件人、发件人、抄送、主题等)配置为消息标头值。开发人员还可以在标头中存储任意键值对。
Message Channel
消息通道表示管道-过滤器体系结构中的"管道"。生产者向通道发送消息,消费者从通道接收消息。因此,消息通道解耦了消息传递组件,还提供了一个方便的截获和监视消息的点。
消息通道可以遵循点对点语义或发布-订阅语义。对于点到点通道,只能有一个使用者接收发送到该通道的每条消息。另一方面,发布-订阅通道试图将每条消息广播给该通道上的所有订阅者。Spring Integration支持这两种模型。
虽然"点对点"和"发布-订阅"定义了最终接收每条消息的消费者数量的两个选项,但还有另一个重要的考虑事项:通道是否应该缓冲消息? 在Spring Integration中,可轮询通道能够缓冲队列中的消息。缓冲的优点是允许对入站消息进行限制,从而防止使用者超载。然而,顾名思义,这也增加了一些复杂性,因为如果配置了轮询器,消费者只能从这样的通道接收消息。另一方面,连接到可订阅通道的消费者只是消息驱动的。消息通道实现(Message Channel Implementations)详细讨论了Spring Integration中可用的各种通道实现。
Message Channel的类型
PublishSubscribeChannel
PublishSubscribeChannel(发布订阅通道)实现将发送给它的任何消息广播到其订阅的所有处理程序。这最常用于发送事件消息,其主要作用是通知(与文档消息相反,文档消息通常由单个处理程序处理)。注意,PublishSubscribeChannel仅用于发送。由于它在调用send(Message)方法时直接向订阅者广播,因此消费者不能轮询消息(它没有实现pollableChannel(轮询通道),因此没有receive()方法)。相反,任何订阅者本身必须是MessageHandler,并且依次调用订阅者的handleMessage(Message)方法。
在3.0版本之前,在没有订阅者的PublishSubscribeChannel上调用send方法将返回false。当与MessagingTemplate一起使用时,会抛出MessageDeliveryException。从3.0版本开始,行为发生了变化,如果至少有最小订阅者存在(并成功处理消息),则始终认为发送成功。可以通过设置minSubscribers属性来修改此行为,该属性的默认值为0。
注意:如果您使用TaskExecutor,则仅使用正确数量的订阅者进行此确定,因为消息的实际处理是异步执行的。
QueueChannel
QueueChannel(队列通道)实现了一个队列存储的封装形式。与PublishSubscribeChannel不同,QueueChannel具有点到点语义。换句话说,即使通道有多个消费者,也只有其中一个应该接收发送到该通道的任何消息。它提供了一个默认的无参数构造函数(提供实质上无限的Integer.MAX_VALUE容量)以及一个接受队列容量的构造函数,如下面的清单所示:
java
public QueueChannel(int capacity)
未达到容量限制的通道将消息存储在其内部队列中,并且send(Message<?>)方法立即返回,即使没有接收器准备好处理该消息。如果队列已达到容量,则发送方阻塞,直到队列中有空间可用。或者,如果使用带有附加超时参数的send方法,则队列将阻塞,直到其中一个空间可用或超时时间结束,以先发生的为准。类似地,如果队列上有可用的消息,receive()调用会立即返回,但是,如果队列为空,则receive调用可能会阻塞,直到消息可用或超时(如果提供的话)结束。在任何一种情况下,都可以通过传递超时值0强制立即返回,而不管队列的状态如何。但是请注意,对send()和receive()版本的调用不带超时参数会无限期阻塞。
PriorityChannel
QueueChannel执行先进先出(FIFO)排序,而PriorityChannel(优先级通道)是另一种实现,它允许在通道内根据优先级对消息进行排序。
缺省情况下,优先级由每条消息中的优先级头决定。但是,对于自定义优先级确定逻辑,可以给PriorityChannel构造函数提供comparator <Message<?>>类型的比较器。
RendezvousChannel
Renzvouschannel(约会通道)支持"直接切换"场景,其中发送方阻塞,直到另一方调用通道的receive()方法。另一方会封锁,直到发送方发送消息。在内部,这个实现与QueueChannel非常相似,除了它使用了SynchronousQueue (同步队列)(BlockingQueue(阻塞队列)的零容量实现)。这在发送方和接收方在不同线程中操作的情况下工作得很好,但在队列中异步删除消息是不合适的。换句话说,使用renderzvouschannel,发送方知道某个接收方已经接受了消息,而使用QueueChannel,消息将被存储到内部队列中,并且可能永远不会收到。
请记住,默认情况下,所有这些基于队列的通道都只在内存中存储消息。当需要持久化时,您可以在"queue"元素中提供"message-store"属性来引用持久的MessageStore实现,也可以将本地通道替换为由持久代理支持的通道,例如jms支持的通道或通道适配器。后一个选项允许您利用任何JMS提供程序的消息持久性实现,如JMS支持中所讨论的那样。但是,当不需要在队列中进行缓冲时,最简单的方法是依赖DirectChannel。
renzvouchannel对于实现请求-应答操作也很有用。发送方可以创建一个临时的匿名的renzvouschannel实例,然后在构建消息时将其设置为'replyChannel'标头。发送该消息后,发送方可以立即调用receive(可选地提供一个超时值),以便在等待回复消息时阻塞。这与许多Spring Integration的请求-应答组件在内部使用的实现非常相似。
DirectChannel(默认使用的通道类型)
DirectChannel(直接通道)具有点对点语义,但与前面描述的任何基于队列的通道实现相比,它更类似于PublishSubscribeChannel。它实现了SubscribableChannel接口而不是PollableChannel接口,因此它直接将消息分发给订阅者。然而,作为点对点通道,它与PublishSubscribeChannel的不同之处在于,它将每个消息发送到单个订阅的MessageHandler。
实际上MessageHandler是一个接口,我们可以根据自己的需求去实现这个接口的handleMessage方法
java
public interface MessageHandler {
void handleMessage(Message<?> message) throws MessagingException;
}
DirectChannel和PublishSubscribeChannel都实现了SubscribableChannel,SubscribableChannel接口有subscribe方法,可以接收MessageHandler对象
java
public interface SubscribableChannel extends MessageChannel {
boolean subscribe(MessageHandler handler);
boolean unsubscribe(MessageHandler handler);
}
因此如果想给DirectChannel或者PublishSubscribeChannel注入自己的业务逻辑,则可以参考类似的代码
java
SubscribableChannel myChannel = new DirectChannel();
myChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
//do something
...
}
});
除了是最简单的点对点通道选项之外,它最重要的特性之一是它允许单个线程在通道的"两侧"执行操作。例如,如果一个处理程序订阅了一个DirectChannel,那么在send()方法调用返回之前,向该通道发送一个消息将触发在发送方线程中直接调用该处理程序的handleMessage(Message)方法。
提供具有此行为的通道实现的主要动机是支持必须跨通道的事务,同时仍然受益于通道提供的抽象和松耦合。如果在事务范围内调用send()调用,则处理程序调用的结果(例如,更新数据库记录)在确定该事务的最终结果(提交或回滚)方面发挥作用。
由于DirectChannel是最简单的选项,并且不会增加调度和管理轮询器线程所需的任何额外开销,因此它是Spring Integration中的默认通道类型。一般思路是为应用程序定义通道,考虑哪些通道需要提供缓冲或限制输入,并将这些通道修改为基于队列的PollableChannels。同样,如果一个通道需要广播消息,它不应该是DirectChannel,而应该是PublishSubscribeChannel。
DirectChannel在内部委托消息调度程序调用其订阅的消息处理程序,并且该调度程序可以具有由负载平衡器(load-balance)或负载平衡器引用(load-balance -ref)属性(互斥)公开的负载平衡策略。消息调度程序使用负载平衡策略来帮助确定当多个消息处理程序订阅同一通道时,如何在消息处理程序之间分发消息。为了方便起见,load-balancer属性公开了一个指向LoadBalancingStrategy已有实现的值枚举。唯一可用的值是round-robin(循环处理程序之间的负载平衡)和none(对于想要显式禁用负载平衡的情况)。其他策略实现可能会在未来的版本中添加。然而,从3.0版本开始,你可以提供你自己的LoadBalancingStrategy的实现,并通过使用load-balance -ref属性注入它,该属性应该指向一个实现LoadBalancingStrategy的bean,如下面的例子所示:
FixedSubscriberChannel是一个SubscribableChannel,它只支持一个不能取消订阅的MessageHandler订阅者。这对于不涉及其他订阅者且不需要通道拦截器的高吞吐量性能用例非常有用。
xml
<int:channel id="lbRefChannel">
<int:dispatcher load-balancer-ref="lb"/>
</int:channel>
<bean id="lb" class="foo.bar.SampleLoadBalancingStrategy"/>
注意load-balancer和load-balance -ref属性是互斥的。
负载平衡还与布尔故障转移属性一起工作。如果故障转移值为true(默认值),则当前面的处理程序抛出异常时,调度程序会退回到任何后续处理程序(根据需要)。顺序由在处理程序本身上定义的可选顺序值决定,如果不存在这样的值,则由处理程序订阅的顺序决定。
如果某种情况要求调度程序总是尝试调用第一个处理程序,然后在每次发生错误时以相同的固定顺序返回,则不应提供负载平衡策略。换句话说,即使没有启用负载平衡,调度程序仍然支持故障转移布尔属性。但是,如果没有负载平衡,则根据处理程序的顺序,总是从第一个处理程序开始调用。例如,当有一级、二级、三级等的明确定义时,这种方法工作得很好。当使用名称空间支持时,端点上的order属性决定了顺序。
请记住,负载平衡和故障转移仅在通道具有多个订阅消息处理程序时应用。当使用名称空间支持时,这意味着多个端点共享在input-channel属性中定义的相同通道引用。 从版本5.2开始,当故障转移为true时,当前处理程序的失败以及失败的消息将分别记录在debug或info(如果分别配置)下。
ExecutorChannel
ExecutorChannel(执行器通道)是一个点对点通道,它支持与DirectChannel相同的分派器配置(负载平衡策略和故障转移布尔属性)。这两种调度通道类型之间的关键区别在于,ExecutorChannel委托给TaskExecutor(任务执行器,通常是一个线程池)的一个实例来执行调度。这意味着发送方法通常不会阻塞,但也意味着处理程序调用可能不会发生在发送方线程中。因此,它不支持跨越发送方和接收方处理程序的事务。
发送者有时会阻塞。例如,当使用带有限制客户端的拒绝策略的TaskExecutor(例如Threadpoolexecutor . Callerrunsppolicy)时,发送方的线程可能在线程池达到最大容量并且执行器的工作队列已满的任何时候执行该方法。由于这种情况只会以不可预测的方式发生,因此您不应该依赖它进行事务处理。
PartitionedChannel
从版本6.1开始,提供了一个PartitionedChannel(分区通道)实现。这是AbstractExecutorChannel的扩展,表示点对点调度逻辑,其中实际消费在特定线程上处理,由从发送到该通道的消息计算的分区键决定。这个通道类似于上面提到的ExecutorChannel,但不同之处在于,具有相同分区键的消息总是在同一线程中处理,从而保持了顺序。它不需要外部的TaskExecutor,但是可以配置一个自定义的ThreadFactory(例如Thread.ofVirtual().name("partition-", 0).factory())。该工厂用于将单线程执行器填充到每个分区的MessageDispatcher委托中。默认情况下,是IntegrationMessageHeaderAccessor。CORRELATION_ID消息头用作分区键。这个通道可以配置为一个简单的bean:
java
@Bean
PartitionedChannel somePartitionedChannel() {
return new PartitionedChannel(3, (message) -> message.getHeaders().get("partitionKey"));
}
通道将有3个分区------专用线程;将使用partitionKey头来确定将在哪个分区中处理消息。有关更多信息,请参阅PartitionedChannel类Javadocs。
FluxMessageChannel
FluxMessageChannel(流量信息通道)是org. reactivesreams .publisher的一个实现,用于将发送的消息"下沉"到内部的reactor.core.publisher.Flux中,供下游响应订阅者按需消费。这个通道实现既不是SubscribableChannel,也不是PollableChannel,所以只有org.reactivestreams.Subscriber实例可以用来从这个通道中消费响应式流的背压特性。另一方面,FluxMessageChannel通过其订阅自(Publisher<Message<?>>)实现了一个ReactiveStreamsSubscribableChannel,该特性允许从响应源发布者接收事件,将响应流桥接到集成流中。为了实现整个集成流的完全响应行为,必须在流中的所有端点之间放置这样一个通道。
有关响应式流交互的更多信息,请参阅响应式流支持。
Scoped Channel
Spring Integration 1.0提供了ThreadLocalChannel实现,但在2.0中已经删除了。现在,处理相同需求的更通用的方法是向通道添加一个scope属性。属性的值可以是上下文中可用的作用域的名称。例如,在web环境中,某些作用域是可用的,并且任何自定义作用域实现都可以在上下文中注册。下面的例子展示了应用于通道的线程局部作用域,包括作用域本身的注册:
xml
<int:channel id="threadScopedChannel" scope="thread">
<int:queue />
</int:channel>
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread" value="org.springframework.context.support.SimpleThreadScope" />
</map>
</property>
</bean>
上一个示例中定义的通道也在内部委托给队列,但是通道绑定到当前线程,因此队列的内容也同样绑定。这样,发送到通道的线程可以稍后接收这些相同的消息,但其他线程无法访问它们。虽然很少需要线程作用域通道,但在DirectChannel实例用于强制执行单个线程操作,但任何回复消息都应发送到"终端"通道的情况下,它们可能很有用。如果该终端通道是线程作用域的,则原始发送线程可以从终端通道收集其应答。
现在,由于任何通道都可以定义作用域,除了thread-Local之外,还可以定义自己的作用域。
Channel Interceptors(通道拦截器)
消息传递体系结构的优点之一是能够提供通用行为,并以非侵入性的方式捕获关于通过系统的消息的有意义的信息。由于Message实例被发送到MessageChannel实例并从MessageChannel实例接收,因此这些通道为拦截发送和接收操作提供了机会。ChannelInterceptor策略接口,如下面的清单所示,为这些操作提供了方法:
java
public interface ChannelInterceptor {
Message<?> preSend(Message<?> message, MessageChannel channel);
void postSend(Message<?> message, MessageChannel channel, boolean sent);
void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex);
boolean preReceive(MessageChannel channel);
Message<?> postReceive(Message<?> message, MessageChannel channel);
void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex);
}
在实现接口之后,用通道注册拦截器只需要进行以下调用:
java
channel.addInterceptor(someChannelInterceptor);
返回Message实例的方法可以用于转换消息,也可以返回'null'以防止进一步处理(当然,任何方法都可以抛出RuntimeException)。此外,preReceive方法可以返回false以阻止接收操作继续进行。
MessagingTemplate
引入端点及其各种配置选项后,Spring Integration为消息传递组件提供了一个基础,支持从消息传递系统对应用程序代码进行非侵入性调用。但是,有时需要从应用程序代码调用消息传递系统。为了方便实现这样的用例,Spring Integration提供了一个MessagingTemplate,它支持跨消息通道的各种操作,包括请求和回复场景。例如,可以发送请求并等待回复,如下所示:
java
MessagingTemplate template = new MessagingTemplate();
Message reply = template.sendAndReceive(someChannel, new GenericMessage("test"));
在前面的示例中,模板将在内部创建一个临时匿名通道。'sendTimeout'和'receiveTimeout'属性也可以在模板上设置,也支持其他交换类型。下面的清单显示了这些方法的签名:
java
public boolean send(final MessageChannel channel, final Message<?> message) { ...
}
public Message<?> sendAndReceive(final MessageChannel channel, final Message<?> request) { ...
}
public Message<?> receive(final PollableChannel<?> channel) { ...
}
Enter the GatewayProxyFactoryBean中描述了一种侵入性较小的方法,它允许您调用带有有效负载或报头值的简单接口,而不是Message实例。
Message Endpoints
消息端点表示管道和过滤器体系结构的"过滤器"。如前所述,端点的主要作用是以非侵入性的方式将应用程序代码连接到消息传递框架。换句话说,理想情况下,应用程序代码应该不知道消息对象或消息通道。这类似于MVC范例中的控制器角色。就像控制器处理HTTP请求一样,消息端点处理消息。正如控制器被映射到URL模式一样,消息端点被映射到消息通道。这两种情况的目标是相同的:将应用程序代码与基础设施隔离开来。在《Enterprise Integration Patterns》一书中详细讨论了这些概念和随后的所有模式。在这里,我们只提供Spring Integration支持的主要端点类型以及与这些类型相关联的角色的高级描述。后面的章节详细介绍并提供了示例代码和配置示例。
Message Transformer
消息转换器负责转换消息的内容或结构,并返回修改后的消息。可能最常见的转换器类型是将消息的有效负载从一种格式转换为另一种格式(例如从XML转换为java.lang.String)的转换器。类似地,转换器可以添加、删除或修改消息的报头值。
一般可以用@Transformer注解来标记自定义的Message Transformer组件,可以添加inputChannel的值和outputChannel的值,表示其输入通道和输出通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Transformer {
String inputChannel() default "";
String outputChannel() default "";
String[] adviceChain() default {};
String sendTimeout() default "";
String autoStartup() default "";
String phase() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
下面是我定义的一个Transformer,用于将收到的payLoad转化为List类型,仅供参考
java
@Transformer(inputChannel = "NodeNameListChannel", outputChannel = "NodeNameTransformerChannel")
public List<String> nodeNameListTransformer(String payload) throws JsonProcessingException {
return objectMapper.readValue(payload, new TypeReference<>() {
});
}
Message Filter
消息筛选器确定是否应该将消息传递到输出通道。这只需要一个布尔测试方法,该方法可以检查特定的有效负载内容类型、属性值、是否存在标头或其他条件。如果消息被接受,则将其发送到输出通道。如果不是,则将其删除(或者,对于更严重的实现,可能会抛出Exception)。消息筛选器通常与发布-订阅通道一起使用,其中多个消费者可能接收到相同的消息,并使用筛选器的标准来缩小要处理的消息集。
一般可以用@Filter注解来标记自定义的Message Filter组件,可以添加inputChannel的值和outputChannel的值,表示其输入通道和输出通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Filter {
String inputChannel() default "";
String outputChannel() default "";
String discardChannel() default "";
String throwExceptionOnRejection() default "";
String[] adviceChain() default {};
String discardWithinAdvice() default "";
String sendTimeout() default "";
String autoStartup() default "";
String phase() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
注意,不要将管道-过滤器体系结构模式中"过滤器"的一般用法与有选择地缩小两个通道之间流动的消息的特定端点类型混淆。"过滤器"的管道和过滤器概念与Spring Integration的消息端点更加匹配:任何可以连接到消息通道以发送或接收消息的组件。
Message Router
消息路由器负责决定接下来哪个或哪个通道(如果有的话)应该接收消息。通常,决策是基于消息的内容或消息头中可用的元数据。消息路由器通常用作服务激活器或其他能够发送应答消息的端点上静态配置的输出通道的动态替代方案。同样,如前所述,消息路由器为多个订阅者使用的响应式消息过滤器提供了一种主动替代方案。
一般可以用@Router注解来标记自定义的Message Router组件,可以添加inputChannel的值,表示其输入通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Router {
String inputChannel() default "";
String defaultOutputChannel() default "";
String[] channelMappings() default {};
String prefix() default "";
String suffix() default "";
String resolutionRequired() default "";
String applySequence() default "";
String ignoreSendFailures() default "";
String sendTimeout() default "";
String autoStartup() default "";
String phase() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
Splitter
拆分器是另一种类型的消息端点,其职责是从其输入通道接受消息,将该消息拆分为多个消息,并将每个消息发送到其输出通道。这通常用于将"复合"有效负载对象划分为包含细分有效负载的消息组。
一般可以用@Splitter注解来标记自定义的Message Splitter组件,可以添加inputChannel的值和outputChannel的值,表示其输入通道和输出通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Splitter {
String inputChannel() default "";
String outputChannel() default "";
String applySequence() default "";
String[] adviceChain() default {};
String sendTimeout() default "";
String autoStartup() default "";
String phase() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
以下是一个Splitter的例子
java
@Splitter
List<LineItem> extractItems(Order order) {
return order.getItems()
}
其他的例子可以参考Advising Endpoints Using Annotations, Splitters 以及 File Splitter
Aggregator
聚合器基本上是拆分器的反向操作,它是一种消息端点,可以接收多个消息并将它们组合成单个消息。实际上,聚合器通常是包含拆分器的管道中的下游消费者。从技术上讲,聚合器比拆分器更复杂,因为它需要维护状态(要聚合的消息),决定何时可用完整的消息组,并在必要时超时。此外,在超时的情况下,聚合器需要知道是发送部分结果、丢弃它们还是将它们发送到单独的通道。Spring Integration提供了一个CorrelationStrategy、一个ReleaseStrategy和可配置的超时设置,是否在超时时发送部分结果,以及一个丢弃通道。
一般可以用@Aggregator注解来标记自定义的Message Aggregator组件,可以添加inputChannel的值和outputChannel的值,表示其输入通道和输出通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Aggregator {
String inputChannel() default "";
String outputChannel() default "";
String discardChannel() default "";
String sendTimeout() default "";
String sendPartialResultsOnExpiry() default "";
String autoStartup() default "";
String phase() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
Service Activator
服务激活器是用于将服务实例连接到消息传递系统的通用端点。必须配置输入消息通道,并且,如果要调用的服务方法能够返回值,还可以提供输出消息通道。
输出通道是可选的,因为每个消息也可以提供自己的"返回地址"报头。同样的规则适用于所有消费者端点。
一般可以用@ServiceActivator注解来标记自定义的Service Activator组件,可以添加inputChannel的值和outputChannel的值,表示其输入通道和输出通道,其他属性可以根据自己的进行填写
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceActivator {
String inputChannel() default "";
String outputChannel() default "";
String requiresReply() default "";
String[] adviceChain() default {};
String sendTimeout() default "";
String autoStartup() default "";
String phase() default "";
String async() default "";
Poller[] poller() default {};
Reactive reactive() default @Reactive("\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n");
}
服务激活器调用某个服务对象上的操作来处理请求消息,提取请求消息的有效负载并进行转换(如果方法不期望消息类型的参数)。每当服务对象的方法返回一个值时,如果需要(如果它还不是消息类型),该返回值同样被转换为回复消息。该应答消息被发送到输出通道。如果没有配置输出通道,则应答将发送到消息的"返回地址"中指定的通道(如果可用)。
请求-应答服务激活器端点将目标对象的方法连接到输入和输出消息通道。
如前所述,在消息通道中,通道可以是可轮询的,也可以是可订阅的。在前面的图表中,这是由"时钟"符号、实线箭头(投票)和虚线箭头(订阅)来描述的。
Channel Adapter
通道适配器是将消息通道连接到其他系统或传输的端点。通道适配器可以是入站的,也可以是出站的。通常,通道适配器在消息和从其他系统接收或发送到其他系统的任何对象或资源(文件、HTTP请求、JMS消息等)之间进行一些映射。根据传输,通道适配器还可以填充或提取消息头值。Spring Integration提供了许多通道适配器,这些适配器将在后面的章节中介绍。
消息源可以是可轮询的(例如,POP3)或消息驱动的(例如,IMAP Idle)。在前面的图中,这是由"时钟"符号、实线箭头(民意调查)和虚线箭头(消息驱动)描述的。
正如前面在消息通道中讨论的,通道可以是可轮询的,也可以是可订阅的。在前面的图表中,这是由"时钟"符号、实线箭头(投票)和虚线箭头(订阅)来描述的。