Intercept作爲一個極其強大的擴展機制,其理念幾乎存在於所有知名框架中,諸如Spring,Mybatis,Tomcat等等都無一例外地提供了相應的支持,在保持自身框架本身整潔的同時,實現對各類業務場景的支持。而我們的Apache Camel也毫不意外地也提供了自己的Intercept實現,本文我們將嘗試對Apache Camel的支持方式和實現邏輯進行一次探究,以期做到熟練運用。
1. 概述
Apache Camel中作爲EIP實現者,諸如"當xxxx的時候,系統需要yyyy"的場景應該是屬於常規需求了。Apache Camel提供了多種方式來完成對其的支持,本文將注意力集中到其中的一種實現方式 —— Intercept。
2. 源碼解讀
首先依然是本次的測試用例:
CamelTestUtil.defaultPrepareTest2(new RouteBuilder() {
@Override
public void configure() throws Exception {
intercept().process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
Console.error("intercept - " + exchange.getIn().getBody());
}
}).id("interceptProcess");
// EmptyProcessor爲一個空的Processor實現, 裏面不作任何操作
from("stream:in?promptMessage=Enter something:")//
// 這裏intercept輸出 鍵入的內容
.setBody(constant("1")).id("setBody-1")//
// 這裏intercept輸出 上面的1
.process(EmptyProcessor.me).id("EmptyProcessor1")//
// 這裏intercept輸出 上面的1
.setBody(constant("2")).id("setBody-2")//
// 這裏intercept輸出 上面的2
.process(EmptyProcessor.me).id("EmptyProcessor2")//
// 這裏intercept輸出 上面的2
.to("stream:err") // 這裏輸出紅色的2
// 這裏輸出 上面的2
.process(CamelLogRecordProcessor.me).id("logProcessor");
}
});
}
本次的源碼解讀按照之前的博客格式,依然是分爲兩部分:
2.1 啓動時
首先讓我們來看看在初始化階段,Apache Camel是如何將Intercept功能組裝進Camel執行鏈條中的。
以上用例啓動之後,有以下兩個比較重要的堆棧信息:
堆棧一(準備intercept實現者InterceptDefinition
):
- 以上截圖自
RouteDefinitionHelper
工具類,筆者特意將註釋部分進行了保留,從中我們可以得知Apache Camel通過將InterceptDefinition
實例作爲output集合中的第一個,實現了在intercept優先於actual route進行處理。這樣就可以實現攔截所有node的目標。 - 圖中字段
intercept
字段指向的實際類型正是InterceptDefinition
。
堆棧二(裝配Intercept功能):
以上截圖中:
output
字段指向的是測試用例配置的intercept()
後配置的匿名Processor
接口實現類。interceptedTarget
字段指向的則是本次Route定義中的node,筆者本次截圖是在第一次命中斷點時候,因此這個node就是setBody()
。- 上圖中斷點命中的位置可以看到 —— 作爲攔截邏輯的匿名
Processor
實現和Route定義節點setBody()
被封裝爲了一個Pipeline實例
(注意兩者的先後順序,這關係到之後的執行順序)。 - 在本次測試用例中,以上截圖中的斷點我們將命中六次,這正好對應測試用例中定義的node數量。
- 跳轉到以上堆棧圖中的
DefaultChannel.initChannel()
中,我們就會發現intercept()
的實現是依賴於接口InterceptStrategy
提供的擴展功能的。這一點從InterceptDefinition
類間接繼承自ProcessorDefinition
,而其對於createProcessor
方法的實現中就可見端倪。
2.2 執行時
在控制檯敲入任意字符,我們將得到如下堆棧:
結合之前的系列博文,加之上一小節的初始化邏輯解析,我們可以很容易地解讀出以上堆棧中表述的邏輯。
輸入任意字符,我們將得到以下輸出結果(結合上面的測試用例中的註釋一起理解):
3. 擴展
其它諸如intercept()
後接when()
來進行條件攔截,或者interceptFrom()
,interceptSendToEndpoint()
,限於篇幅原因本次就不再詳述了,讀者感興趣的可以自行閱讀相關源碼或者下方給出的官方鏈接。
4. 總結
- 對應intercept(),Apache Camel內部使用
InterceptDefinition
類來統籌整個配置組裝初始化的工作。而具體的實現邏輯則依賴於接口InterceptStrategy
提供的擴展功能。這一點可以從InterceptDefinition
類間接繼承自ProcessorDefinition
,而其對於createProcessor
方法的實現中就可見端倪。 - 對應intercept(),其執行時機位於匹配node執行之前。正如上面解析的,Apache Camel使用Pipeline來將intercept功能和用戶自定義node組裝在一起,確保每個node攔截邏輯的獨立性。
5. Links
- Office Site - Intercept
- Apache Camel源碼研究之InterceptStrategy
- Apache Camel源碼研究之ProcessorDefinition
- 《Camel In Action》 P178, 288, P377