|
如需使用最新稳定版本,请使用 Spring Integration 7.0.4! |
通道拦截器
消息架构的一个优势是能够以非侵入的方式提供通用行为,并捕获系统中传递的消息的有意义信息。
由于Message实例发送到和从MessageChannel实例接收,因此这些通道提供了拦截发送和接收操作的机会。
如以下列表所示,ChannelInterceptor策略接口提供每个操作的方法:
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);
}
在实现接口后,将拦截器注册到通道上只需进行以下调用:
channel.addInterceptor(someChannelInterceptor);
返回 Message 实例的方法可用于转换 Message,或者返回 'null' 以阻止进一步处理(当然,任何方法都可能抛出 RuntimeException)。
此外,preReceive 方法也可以返回 false 以阻止接收操作继续进行。
请记住,receive() 次调用仅与 PollableChannels 相关。
实际上,SubscribableChannel 接口甚至没有定义 receive() 方法。
其原因在于,当向 SubscribableChannel 发送 Message 时,它会根据通道类型(例如,PublishSubscribeChannel 会发送给其所有订阅者)直接发送给零个或多个订阅者。
因此,preReceive(…)、postReceive(…) 和 afterReceiveCompletion(…) 拦截器方法仅在拦截器应用于 PollableChannel 时才会被调用。 |
Spring Integration 还提供了一个 Wire Tap 模式的实现。
它是一个简单的拦截器,将 Message 发送到另一个通道而不改变现有的流程。
它可以非常有用用于调试和监控。
一个示例可以在 Wire Tap 中看到。
因为很少需要实现拦截器方法的所有功能,该接口提供了无操作的方法(返回void的方法没有代码,返回Message的方法直接返回Message,而返回boolean的方法返回true)。
拦截器方法的调用顺序取决于通道类型。如前面所述,基于队列的通道是唯一一个在最初就被拦截了receive()方法的地方。另外,发送和接收拦截之间的关系取决于单独的发送线程和接收线程的时间安排。例如,如果接收者已经在等待消息时被阻塞,则顺序可能是:preSend,preReceive,postReceive,postSend。然而,如果接收方在发送方将消息放入通道并返回后进行轮询,顺序将如下所示:preSend、postSend(经过一段时间)、preReceive、postReceive。在这种情况下所经过的时间取决于多个因素,因此通常是不可预测的(事实上,接收操作可能永远不会发生)。队列的类型也起着作用(例如,rendezvous 与优先级)。简而言之,除了 preSend 在 postSend 之前、preReceive 在 postReceive 之前这一事实外,你不能依赖其他顺序。
|
从 Spring Framework 4.1 和 Spring Integration 4.1 开始,ChannelInterceptor 提供了新的方法:afterSendCompletion() 和 afterReceiveCompletion()。 它们在 send()' and 'receive() 调用之后被调用,无论是否抛出任何异常,这允许进行资源清理。 请注意,通道会按照与初始 preSend() 和 preReceive() 调用相反的顺序,在 ChannelInterceptor 列表上调用这些方法。
自 Spring Framework 5.1 版本起,全局频道拦截器现在也适用于动态注册的频道——例如通过使用 beanFactory.initializeBean() 或 IntegrationFlowContext 初始化 Java DSL 中定义的 beans。
此前,当应用上下文刷新后创建的 beans 不会应用拦截器。
从5.1版本开始,如果没有收到消息不再调用ChannelInterceptor.postReceive();因此,再不需要检查null或Message<?>。
以前的方法会调用。如果您有一个依赖于之前行为的拦截器,请实现afterReceiveCompleted(),因为无论是否接收到消息,该方法都会被调用。
从 5.2 版本开始,ChannelInterceptorAware 已弃用,现在出于向后兼容性考虑,它扩展了来自 Spring Messaging 模块的 InterceptableChannel。 |