通道拦截器
消息架构的优势之一在于,它能够以非侵入的方式提供通用行为,并捕获系统中传递的消息的有意义信息。由于 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 操作的继续执行。
请注意,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。这种情况下经过的时间取决于多种因素,因此通常无法预测(实际上,receive 可能永远不会发生)。队列的类型也起到一定作用(例如,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() 初始化的bean,或在使用Java DSL时通过 IntegrationFlowContext 注册的通道。在此之前,当bean在应用上下文刷新后创建时,拦截器不会被应用。
此外,从版本5.1开始,当没有接收到消息时,ChannelInterceptor.postReceive() 不再被调用;因此不再需要检查 Message<?> 是否为 null。在此之前,该方法会被调用。如果你的拦截器依赖于之前的行为,请改为实现 afterReceiveCompleted(),因为无论是否接收到消息,该方法都会被调用。
从 5.2 版本开始,ChannelInterceptorAware 已被弃用,转而支持 Spring Messaging 模块中的 InterceptableChannel,为了向后兼容,ChannelInterceptorAware 现在扩展了 InterceptableChannel。