本文主要就Apache Camel提供的SpringBoot支持作一些分析和深入瞭解,方便以後更加靈活地應用。
1. 概述
本文主要主要關注Apache Camel集成SpringBoot時候提供的三方面功能:AutoConfig,Actuate以及其他一些細節。至於SpringBoot的初步入門相關配置,讀者可以直接查閱本文底部給出的官方文檔鏈接,這裏就不多贅述了。
2. 自動化配置
關於SpringBoot的自動化配置原理,可以參見筆者之前的一篇博客SpringBoot源碼解析之AutoConfiguration,因此本小節接下來我們將直接切入主題,看看Apache Camel的相關處理邏輯。
通過翻閱camel-spring-boot-2.21.3.jar
源碼下的META-INF/spring.factories
文件,我們可以定位到相關類CamelAutoConfiguration
,下面將列出由其定義的創建核心Bean的幾個方法。
2.1 camelContext()
該方法負責創建在Apache Camel起着靈魂中樞作用的CamelContext
實例(這裏無疑就是SpringCamelContext
了),對於該方法,推薦讀者仔細閱讀下其回調的doConfigureCamelContext()
方法——該方法中對CamelContext
提供的諸多擴展點進行了詳盡的示例,通過閱讀這些實現,你可以對CamelContext
的擴展機制有一個全面的瞭解,也就能爲未來的需求變更打下非常好的基礎。
也正是因爲此方法註冊到Spring容器中的CamelContext
實例,我們可以在需要的位置直接使用以下代碼來獲取其實例以完成相關需求。
@Autowired
private CamelContext camelContext;
2.2 camelBeanPostProcessor()
Apache Camel作爲一款獨立的框架,不可避免地定義了一些自己的IOC註解,而在Spring環境下,這些註解所聲明的依賴關係的維護正是通過本方法所返回的CamelBeanPostProcessor
實例來完成的。
被注入到Spring容器中的CamelBeanPostProcessor
實例通過實現鼎鼎大名的BeanPostProcessor
接口來爲容器中的Bean注入Camel註解所代表的相應依賴。
注入依賴操作的實現中,CamelBeanPostProcessor
自身只起到一個適配的作用,真正的注入操作是委託給了camel-core-2.21.3.jar
自身實現的DefaultCamelBeanPostProcessor
類。感興趣的讀者可以自行查閱。
2.3 propertiesParser()
和 properties()
這兩個方法之所以被合併討論,其實光看名字就能猜到幾分。沒錯這兩個Bean的作用正是爲了在Apache Camel中使用自身規定的Placeholder語法來讀取Spring中存儲的用戶自定義配置信息,避免重複性地配置配置文件。
propertiesParser()
方法中創建的SpringPropertiesParser
實例,其實現的正是設計模式中的適配器模式,將對Camel配置信息讀取接口的調用委託給Spring中的相關實現類去完成。
這樣我們就可以用如下的方式讀取application.properties
中的配置信息:
@Autowired
private PropertiesComponent pc;
@Autowired
private CamelContext camelContext ;
camelContext.resolvePropertyPlaceholders("{{camel.springboot.name}}");
pc.parseUri("pc.parseUri(key)");
2.4 producerTemplate()
和 consumerTemplate()
關於這兩個方法的返回值分別爲ProducerTemplate
和ConsumerTemplate
實例。有使用過spring-jdbc等組件的讀者應該非常眼熟。沒錯,Apache Camel爲了減輕使用者的記憶負擔,保持一致性,而直接借鑑了Spring的命名規則。而且這兩個Bean都屬於延遲創建,只在需要的時候纔會進行組裝。
因爲這兩個方法的存在,我們就可以在需要的位置直接使用以下代碼來獲取其實例以完成相關需求。
@Autowired
private ProducerTemplate pt;
Object result = pt.requestBody("direct:start", "hello world");
2.5 routesCollector()
對於SpringBoot環境下配置過Camel Route的朋友應該都知道,這個過程是非常簡單的,只需要繼承自Camel提供的RouteBuilder
抽象類,並實現其configure()
方法,剩下的框架將全權由框架來完成。而本小節將要討論的routesCollector()
方法的返回值RoutesCollector
實例正是關鍵所在。
RoutesCollector
類通過實現Spring提供的擴展接口ApplicationListener<ContextRefreshedEvent>
,來達成在系統初始化完畢後,將Spring容器中註冊的RoutesBuilder
Bean添加到CamelContext中,省卻了研發人員手工維護相應關係的枯燥勞動。
而且觀察其實現,我們發現Camel還提供了兩個相關配置camel.springboot.javaRoutesExcludePattern
和camel.springboot.javaRoutesIncludePattern
來更加精密地控制應該載入哪些Route到CamelContext中。
3. Actuator
在看完了Camel中自動化配置相關的細節之後,我們再來看看Camel提供的SpringBoot Actuator功能,關於SpringBoot Actuator,可以翻閱下筆者之前寫過的一篇相關文章SprinBoot源碼研究之Actuator。本小節依然不再關注SpringBoot Actuator的相關實現細節,而將目光集中到Camel做的改動上。
本小節將主要關注提供Route相關操作的CamelRoutesEndpoint
,其他類只會做簡單的說明。
關於CamelRoutesEndpoint
,其相關的類還有如下幾個:
CamelRoutesMvcEndpoint
,負責將CamelRoutesEndpoint
使用適配器模式暴露爲一個MvcEndpoint
。CamelRoutesEndpointAutoConfiguration
,這個看名字都能猜到又是個AutoConfig。
通觀這三個類,再結合之前的博客SprinBoot源碼研究之Actuator裏的討論,我們不難想到,Camel默認是提供瞭如下訪問接口來讓我們可以控制和知曉Route信息的:
Uri | 用途 | 用途 |
---|---|---|
/camel/routes |
GET | 獲取當前CamelContext下所有的route的相關信息 |
/camel/routes/{id}/detail |
GET | 獲取某個Route的詳情 |
/camel/routes/{id}/dump |
GET | 獲取某個Route的XML格式定義 |
/camel/routes/{id}/info |
GET | 獲取某個Route的大致信息 |
/camel/routes/{id}/reset |
POST | 重置某個Route |
/camel/routes/{id}/resume |
POST | 恢復運行某個Route |
/camel/routes/{id}/start |
POST | 啓動某個Route |
/camel/routes/{id}/stop |
POST | 停止某個Route |
/camel/routes/{id}/suspend |
POST | 掛起某個Route |
最後讓我們來看看以上這些對外接口的後臺相應代碼的位置——除了/camel/routes
之外,其他的接口相應代碼都位於CamelRoutesMvcEndpoint
類中。
而對於/camel/routes
的響應,筆者這裏額外提及一下,因爲稍微有些繞:
CamelRoutesMvcEndpoint
的基類AbstractCamelMvcEndpoint<T extends Endpoint>
在其構造函數中約定了子類實例化時候必須傳入訪問路徑,也就是這裏的/camel/routes
。- 對於
AbstractCamelMvcEndpoint<T extends Endpoint>
,其直接繼承自SpringBoot Actuator中的EndpointMvcAdapter
,而EndpointMvcAdapter
通過覆寫基類的invoke()
方法,將該方法和SpringMVC進行了關聯——註解方式標註必須使用Get訪問方式,方法實現則是簡單地回調基類AbstractEndpointMvcAdapter<E extends Endpoint<?>>
的invoke()
實現。 - 而在
AbstractEndpointMvcAdapter<E extends Endpoint<?>>.invoke()
的實現中,也只是簡單地回調了Endpoint<T>
類型的delegate字段的同名invoke()
方法。所以實際的邏輯是由delegate來完成的。 - 而在本場景下,這個delegate爲
CamelRoutesEndpoint
實例,所以這個invoke()
方法也就是CamelRoutesEndpoint
中實現的了。
4. 其他細節
- 在源碼閱讀過程中,筆者發現了兩個比較有意思的工具類:
ModelHelper.dumpModelAsXml()
,其作用是將內存的中的Route定義以XML字符串的形式打印出來,這就給與了我們一個思路——使用Java DSL來定義Route以享受Java語言強類型的福利,後期轉入維護階段之後使用該方法將Route定義轉移到Spring中,一舉兩得。MessageHelper.dumpMessageHistoryStacktrace()
,這個方法的主要作用是打印出當前Exchange的Message History。實乃後期進行問題追溯,統計報表生成等需求實現的強力輔助工具。
- 通過
camel.springboot.useMdcLogging=true
我們可以開啓SLF4J的MDC功能,這樣我們就可以在日誌信息中使用Camel提供的一些信息,可選的KEY值可以參見MDCUnitOfWork
類中的常量字段。另外可參見官方文檔:mdc-logging。