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