通道攔截器

訊息架構的優勢之一在於能夠以無侵入的方式提供通用行為,並捕獲流經系統的訊息的有意義資訊。由於 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() 方法。這是因為當訊息傳送到 SubscribableChannel 時,它直接傳送給零個或多個訂閱者,具體取決於通道型別(例如,PublishSubscribeChannel 傳送給所有訂閱者)。因此,preReceive(…​)postReceive(…​)afterReceiveCompletion(…​) 攔截器方法僅在攔截器應用於 PollableChannel 時被呼叫。

Spring Integration 還提供了 Wire Tap(竊聽器) 模式的實現。它是一個簡單的攔截器,將 Message 傳送到另一個通道,而不會以其他方式改變現有流程。這對於除錯和監控非常有用。一個示例參見 Wire Tap

由於很少需要實現所有攔截器方法,該介面提供了無操作(no-op)方法(返回 void 的方法沒有程式碼,返回 Message 的方法原樣返回 Message,而返回 boolean 的方法返回 true)。

攔截器方法的呼叫順序取決於通道型別。如前所述,基於佇列的通道是唯一首先會攔截 receive() 方法的通道。此外,傳送和接收攔截之間的關係取決於獨立的傳送者和接收者執行緒的時間安排。例如,如果接收者在等待訊息時已被阻塞,順序可能是這樣:preSend, preReceive, postReceive, postSend。然而,如果傳送者將訊息放入通道並已返回後,接收者才進行輪詢,順序將是:preSend, postSend (經過一段時間), preReceive, postReceive。在這種情況下經過的時間取決於多種因素,因此通常是不可預測的(事實上,接收可能永遠不會發生)。佇列的型別也起作用(例如,會合佇列與優先順序佇列)。簡而言之,除了 preSendpostSend 之前以及 preReceivepostReceive 之前這一事實之外,你不能依賴於順序。

從 Spring Framework 4.1 和 Spring Integration 4.1 開始,ChannelInterceptor 提供了新方法:afterSendCompletion()afterReceiveCompletion()。無論是否發生任何異常,它們都會在 send()receive() 呼叫之後被呼叫,以便進行資源清理。請注意,通道在 ChannelInterceptor 列表上以與初始 preSend()preReceive() 呼叫相反的順序呼叫這些方法。

從 5.1 版本開始,全域性通道攔截器現在也適用於動態註冊的通道——例如,透過使用 beanFactory.initializeBean() 初始化或在使用 Java DSL 時透過 IntegrationFlowContext 初始化的 bean。以前,在應用上下文重新整理後建立 bean 時,攔截器不會被應用。

此外,從 5.1 版本開始,當沒有接收到訊息時,不再呼叫 ChannelInterceptor.postReceive();不再需要檢查 nullMessage<?>。以前,該方法會被呼叫。如果你有一個依賴於先前行為的攔截器,請轉而實現 afterReceiveCompleted(),因為無論是否接收到訊息,都會呼叫該方法。

從 5.2 版本開始,ChannelInterceptorAware 已被棄用,轉而使用 Spring Messaging 模組中的 InterceptableChannel,為了向後相容,前者現在擴充套件了後者。