通道攔截器

訊息傳遞架構的優勢之一,是以非侵入性的方式提供常見行為並擷取通過系統的訊息相關的有意義資訊的能力。由於 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 中。

由於幾乎不需要實作所有攔截器方法,因此介面提供 no-op 方法(傳回 void 的方法沒有程式碼,傳回 Message 的方法按原樣傳回 Message,而 boolean 方法傳回 true)。

攔截器方法的調用順序取決於通道的類型。如先前所述,基於佇列的通道是第一個攔截 receive() 方法的通道。此外,傳送和接收攔截之間的關係取決於單獨的傳送者和接收者執行緒的時序。例如,如果接收者在等待訊息時已被封鎖,則順序可能如下:preSendpreReceivepostReceivepostSend。但是,如果接收者在傳送者已將訊息放在通道上並已傳回之後輪詢,則順序將如下:preSendpostSend(經過一段時間)、preReceivepostReceive。在這種情況下經過的時間取決於許多因素,因此通常是不可預測的(實際上,接收可能永遠不會發生)。佇列的類型也起作用(例如,會合與優先順序)。簡而言之,除了 preSend 先於 postSendpreReceive 先於 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,現在為了向後相容性而擴充了它。