通道拦截器

消息架构的优势之一在于能够以非侵入式的方式提供常见行为并捕获有关通过系统传递的消息的有意义的信息。由于 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 返回方法按原样返回 Messageboolean 方法返回 true)。

拦截器方法的调用顺序取决于通道的类型。如前所述,基于队列的通道是唯一在其中首先拦截 receive() 方法的通道。此外,发送和接收拦截之间的关系取决于单独发送方和接收方线程的时序。例如,如果接收方在等待消息时已阻塞,则顺序可以如下所示:preSendpreReceivepostReceivepostSend。但是,如果接收方在发送方将消息放在通道上并已返回后轮询,则顺序将如下所示:preSendpostSend(经过一段时间)、preReceivepostReceive。在这种情况下经过的时间取决于许多因素,因此通常是不可预测的(事实上,接收可能永远不会发生)。队列的类型也起着作用(例如,会面与优先级)。简而言之,除了 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 初始化的 Bean。以前,在刷新应用程序上下文后创建 Bean 时不会应用拦截器。

此外,从 5.1 版本开始,当未收到消息时,将不再调用 ChannelInterceptor.postReceive();不再需要检查 null Message<?>。以前,会调用该方法。如果您有一个依赖于以前行为的拦截器,请改而实现 afterReceiveCompleted(),因为无论是否收到消息,都会调用该方法。

从 5.2 版本开始,ChannelInterceptorAware 已弃用,取而代之的是 Spring Messaging 模块中的 InterceptableChannel,它现在为了向后兼容而扩展了该模块。