Channel Interceptors

消息架构的一个优势是可以提供公共行为,并通过非侵入式方式捕获有关通过系统传递的消息的有意义的信息。由于消息实例已发送到并从 MessageChannel 实例接收,因此这些通道提供了拦截发送和接收操作的机会。以下清单中所示的 ChannelInterceptor 策略接口提供了这些操作的各个方法:

One of the advantages of a messaging architecture is the ability to provide common behavior and capture meaningful information about the messages passing through the system in a non-invasive way. Since the Message instances are sent to and received from MessageChannel instances, those channels provide an opportunity for intercepting the send and receive operations. The ChannelInterceptor strategy interface, shown in the following listing, provides methods for each of those operations:

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);
}

在实现该接口后,只需调用以下内容,即可将拦截器与频道注册:

After implementing the interface, registering the interceptor with a channel is just a matter of making the following call:

channel.addInterceptor(someChannelInterceptor);

可以将返回 Message 实例的方法用于转换 Message,也可以返回 null 来防止进一步处理(当然,任何方法都可以抛出 RuntimeException)。此外,preReceive 方法可以返回 false 以防止接收操作继续进行。

The methods that return a Message instance can be used for transforming the Message or can return 'null' to prevent further processing (of course, any of the methods can throw a RuntimeException). Also, the preReceive method can return false to prevent the receive operation from proceeding.

请记住,receive() 调用仅与 PollableChannels 相关。事实上,SubscribableChannel 接口甚至没有定义 receive() 方法。原因是当 Message 发送到 SubscribableChannel 时,它会直接发送到零个或多个订阅者,具体取决于通道类型(例如,PublishSubscribeChannel 会发送到其所有订阅者)。因此,只有将拦截器应用于 PollableChannel 时,才会调用 preReceive(…​)postReceive(…​)afterReceiveCompletion(…​) 拦截器方法。

Keep in mind that receive() calls are only relevant for PollableChannels. In fact, the SubscribableChannel interface does not even define a receive() method. The reason for this is that when a Message is sent to a SubscribableChannel, it is sent directly to zero or more subscribers, depending on the type of channel (for example, a PublishSubscribeChannel sends to all of its subscribers). Therefore, the preReceive(…​), postReceive(…​), and afterReceiveCompletion(…​) interceptor methods are invoked only when the interceptor is applied to a PollableChannel.

Spring Integration 还提供了 Wire Tap 模式的实现。这是一个简单的拦截器,它将 Message 发送到另一个通道,而不会以其他方式更改现有流。它对于调试和监控非常有用。在 Wire Tap 中显示一个示例。

Spring Integration also provides an implementation of the Wire Tap pattern. It is a simple interceptor that sends the Message to another channel without otherwise altering the existing flow. It can be very useful for debugging and monitoring. An example is shown in Wire Tap.

由于很少需要实现所有拦截器方法,因此该接口提供了无操作方法(返回 void 方法没有代码,返回消息的方法按原样返回消息,布尔方法返回 true)。

Because it is rarely necessary to implement all of the interceptor methods, the interface provides no-op methods (those returning void method have no code, the Message-returning methods return the Message as-is, and the boolean method returns true).

拦截器方法调用的顺序取决于通道的类型。如前所述,基于队列的通道是唯一最初拦截 receive() 方法的通道。此外,发送和接收拦截之间的关系取决于独立的发送者和接收者线程的时序。例如,如果接收者已在等待消息时被阻止,则顺序可以如下所示:preSendpreReceivepostReceivepostSend。但是,如果接收者在发送者将消息放在通道并已返回后轮询,则顺序将如下所示:preSendpostSend(某一时间间隔)preReceivepostReceive。这种情况下经过的时间取决于许多因素,因此通常是不可预测的(事实上,接收可能永远不会发生)。队列类型也起作用(例如,集合点与优先级)。总之,你不能依靠该顺序,除了 preSendpostSend 之前,preReceivepostReceive 之前。

The order of invocation for the interceptor methods depends on the type of channel. As described earlier, the queue-based channels are the only ones where the receive() method is intercepted in the first place. Additionally, the relationship between send and receive interception depends on the timing of the separate sender and receiver threads. For example, if a receiver is already blocked while waiting for a message, the order could be as follows: preSend, preReceive, postReceive, postSend. However, if a receiver polls after the sender has placed a message on the channel and has already returned, the order would be as follows: preSend, postSend (some-time-elapses), preReceive, postReceive. The time that elapses in such a case depends on a number of factors and is therefore generally unpredictable (in fact, the receive may never happen). The type of queue also plays a role (for example, rendezvous versus priority). In short, you cannot rely on the order beyond the fact that preSend precedes postSend and preReceive precedes postReceive.

从 Spring Framework 4.1 和 Spring Integration 4.1 开始,ChannelInterceptor 提供了新的方法:afterSendCompletion() 和 afterReceiveCompletion()。无论引发任何异常,它们都会在 send() 和 receive() 调用之后调用,从而允许资源清理。请注意,频道以相反的顺序调用 ChannelInterceptor 列表中的这些方法,即初始 preSend() 和 preReceive() 调用。

Starting with Spring Framework 4.1 and Spring Integration 4.1, the ChannelInterceptor provides new methods: afterSendCompletion() and afterReceiveCompletion(). They are invoked after send()' and 'receive() calls, regardless of any exception that is raised, which allow for resource cleanup. Note that the channel invokes these methods on the ChannelInterceptor list in the reverse order of the initial preSend() and preReceive() calls.

从版本 5.1 开始,全局频道拦截器现在适用于动态注册的频道——例如,在使用 Java DSL 时,通过使用 beanFactory.initializeBean() 或 IntegrationFlowContext 初始化的 bean。以前,在刷新应用程序上下文后创建 bean 时不应用拦截器。

Starting with version 5.1, global channel interceptors now apply to dynamically registered channels - such as through beans that are initialized by using beanFactory.initializeBean() or IntegrationFlowContext when using the Java DSL. Previously, interceptors were not applied when beans were created after the application context was refreshed.

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

Also, starting with version 5.1, ChannelInterceptor.postReceive() is no longer called when no message is received; it is no longer necessary to check for a null Message<?>. Previously, the method was called. If you have an interceptor that relies on the previous behavior, implement afterReceiveCompleted() instead, since that method is invoked, regardless of whether a message is received or not.

从 5.2 版开始,ChannelInterceptorAware 已弃用,取而代之的是来自 Spring Messaging 模块的 InterceptableChannel,它现在为了向后兼容性对其进行了扩展。

Starting with version 5.2, the ChannelInterceptorAware is deprecated in favor of InterceptableChannel from the Spring Messaging module, which it extends now for backward compatibility.