通道拦截器
消息架构的优势之一在于能够以非侵入式的方式提供通用行为并捕获有关通过系统传递的消息的有意义的信息。由于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() 方法。原因是,当Message 发送到SubscribableChannel 时,它会直接发送到零个或多个订阅者,具体取决于通道类型(例如,PublishSubscribeChannel 会发送给所有订阅者)。因此,只有当拦截器应用于PollableChannel 时,才会调用preReceive(…) 、postReceive(…) 和afterReceiveCompletion(…) 拦截器方法。 |
Spring Integration还提供了Wire Tap模式的实现。这是一个简单的拦截器,它将Message
发送到另一个通道,而不会以其他方式改变现有流程。它对于调试和监控非常有用。示例请参见Wire Tap。
由于很少需要实现所有拦截器方法,因此该接口提供了无操作方法(返回void
的方法没有代码,返回Message
的方法按原样返回Message
,而boolean
方法返回true
)。
拦截器方法的调用顺序取决于通道类型。如前所述,基于队列的通道是唯一一个首先拦截receive() 方法的通道。此外,发送和接收拦截之间的关系取决于单独的发送方和接收方线程的计时。例如,如果接收方在等待消息时已被阻塞,则顺序可能如下:preSend 、preReceive 、postReceive 、postSend 。但是,如果发送方已将消息放入通道并已返回后,接收方进行轮询,则顺序将如下:preSend 、postSend (一段时间后)、preReceive 、postReceive 。在这种情况下经过的时间取决于许多因素,因此通常是不可预测的(实际上,接收可能永远不会发生)。队列的类型也起作用(例如,会合与优先级)。简而言之,除了preSend 在postSend 之前,preReceive 在postReceive 之前之外,您不能依赖于顺序。 |
从Spring Framework 4.1和Spring Integration 4.1开始,ChannelInterceptor
提供了新的方法:afterSendCompletion()
和afterReceiveCompletion()
。无论引发任何异常,它们都会在send()
和receive()
调用后被调用,这允许进行资源清理。请注意,通道按初始preSend()
和preReceive()
调用的反向顺序调用ChannelInterceptor
列表上的这些方法。
从5.1版本开始,全局通道拦截器现在适用于动态注册的通道——例如通过使用beanFactory.initializeBean()
或IntegrationFlowContext
(使用Java DSL时)初始化的bean。以前,在应用程序上下文刷新后创建bean时,不会应用拦截器。
此外,从5.1版本开始,当没有接收到消息时,不再调用ChannelInterceptor.postReceive()
;不再需要检查null
Message<?>
。以前,会调用该方法。如果您有一个依赖于先前行为的拦截器,请改而实现afterReceiveCompleted()
,因为无论是否接收消息,都会调用该方法。
从5.2版本开始,ChannelInterceptorAware 已弃用,取而代之的是Spring Messaging模块中的InterceptableChannel ,现在为了向后兼容性而扩展它。 |