1. Dubbo(一)——Dubbo 集成於 Spring 的原理
最近一直在看bbo的源碼部分。在閱讀的時候,需要有一個入手點,才能一點一點的進行下去。自己在研究的時候,發現思緒比較亂,於是就以 芋道源碼 為基礎,一點一點的啃食。芋道源碼是直接從bbo的配置和一些核心的API開始講起,是從bbo已經啟動的過程作為開始節點,而這些核心 API 與 Spring 的之間的關系被省略了,這些東西對我來說屬於前置的知識點,所以花了比較長的時間又從 Dubbo 的核心 API 倒著往前看。
在閱讀 Dubbo 時,發現前置知識越來越多,如:Spring 的 refresh 中的一些核心點,Spring 中 bean 的生命周期,BeanFactory 與 FactoryBean 的區別等。所以這些前置知識花了特別多的時間去補。所幸,雖然補前置知識雖然時間長,但是性價比還是可以的。Dubbo 是依賴於Spring 的上下文環境的框架,其他依賴於 Spring 的框架也是相同的道理。Spring 的一些對外的擴展點,讀過之後也會心中有數。
1、本篇主要是描述了 Dubbo 在 Spring 創建上下文的時候,是如何從創建,到能完整提供一個RPC調用能力的一些相關點。
2、由於源碼比較多,直接貼斷點也太過臃腫,所以僅僅貼一些關鍵點來概括整個流程。
3、本文是依賴於前面的 bbo 項目進行斷點分析,項目結構可以參照這里。項目中 bbo 的配置方式是 xml 文件,所以本篇主要說 xml 配置方式。其他方式道理相同,並不是問題的關鍵點。
4、項目啟動的是 bbo-user 服務,所以 UserService 為 bbo:service,OrderService 為 bbo:reference。
下圖為Spring 啟動時是如何載入 Dubbo 的,其中省略了大量過程,只保留了一些關鍵節點,省略的部分可以略微腦補一下。
整個流程的入口是 Spring 的 refresh 方法。每個方法都有比較深的調用棧。與 Dubbo 有關的入口是 refresh 中的 方法
這個方法是執行 beanFactory 的一些後處理操作,其核心流程為在Spring容器中找出實現了BeanFactoryPostProcessor介面的processor並執行。Spring容器會委託給的方法執行。
是比較核心的類,在這里我們關注一下這個類。它的作用是對項目中配置的類進行處理。具體處理可以分為幾步:
在載入類信息時,spring 會去用各種方式掃到注冊的 bean 信息。我們在 spring 中注冊的 bean,逃不出這個方法的掃描方式。 核心方法是:
掃描之後,會將掃描到的 bean 注冊到 beanDefinitionMap 中
首先是此處 org.springframework.beans.factory.xml.#parseBeanDefinitions,可以看出方法會以配置文件根節點起,遍歷所有子節點。
其次是這里 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition), 此方法會通過解析出來的節點,獲取對應的 Spring 的 namespaceUri ,進而獲取對應的配置文件處理器。
此處 ele 參數實際值為 <bbo:service ... />,namespaceUri 為 http://code.alibabatech.com/schema/bbo
我們看一下 resolve 方法中的細節。因為這個方法內部才是 Dubbo 依賴於 Spring 的關鍵點。
此處的 NamespaceHandler 為 DubboNamespaceHandler,再創建結束之後,進行 init 初始化。
可以看到,DubboNamespaceHandler 在初始化的時候,會創建所有 bbo 標簽對應的Config 類的 DubboBeanDefinitionParser。並將 DubboBeanDefinitionParser 和 對應的 bbo 標簽類注冊到 NamespaceHandlerSupport 的 parsers 中。
最後,會在 com.alibaba.bbo.config.spring.schema.DubboBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.Class<?>, boolean) 方法中進行處理
Dubbo 服務比較特殊,beanDefinition 跟普通的 bean 不太一樣。在向 beanDefinitionMap 注冊時,普通的 beanDefinition 的 beanName 與 beanClass 是對應的;而 bbo 服務的 beanDefinition 的 beanName 是bbo 服務的名稱,beanClass 為 bbo 對應的 Bean。
普通的 beanDefinition:
bbo 引用的服務的 beanDefinition:
這一步的核心流程是從 beanFactory 中獲取所有的 ApplicationListener,然後注冊到監聽器集合中。它的關鍵點其實是 ServiceBean。因為 ServiceBean 是 ApplicationListener 的實現。
所以 beanFactory 中 ServiceBean 也會被注冊到監聽器集合中。項目中的 ServiceBean 的 beanClass 實際是 UserService。
這一步的核心點,主要是創建剩餘的各類對象,並將其保存到 singletonObjects 中。其中關聯的前置知識為 Spring 中 bean 的生命周期 。它的核心方法是:
org.springframework.beans.factory.support.#doCreateBean
它的具體流程為:
ps:此處並不是只有這一步才會跟 bean 生命周期相關,bean 生命周期貫穿在 refresh 的很多流程中,只要執行doGetBean 方法,都會走這個流程。此處僅僅借樓關聯一下。
這一步的核心點,是通知所有的監聽器上下文刷新結束的事件。在這一步執行時,會通知到 ServiceBean。
此處暴露的是 UserService。
Dubbo 的啟動條件是完全依賴於 Spring 的啟動流程,Spring 的啟動流程中核心的點是 refresh 方法。所以只要搞懂 refresh 方法,其他的拓展框架流程也會明白。只不過關聯的知識點太多了,還是需要時間的積累才能一點一點的搞懂。
如果本篇有描述不清,或者描述有誤的地方,還望在下方留言,大家一起交流,一起學習,一起進步~
2. Dubbo啟動源碼解析一
這次講 bbo-spring-boot-starter 啟動方式,所以入口就是Spring的SPI機制;
首先在META-INF/spring.factories配置下,配置了org.apache.bbo.spring.boot.autoconfigure.DubboAutoConfiguration類,在啟動時,則會把DubboAutoConfiguration類注冊到spring容器中;
我們來看下DubboAutoConfiguration
先看啟動流程
我們先看下生產者端的啟動流程,首先是在Spring中注冊類
該類實現了介面,則在Spring容器初始化時,會調用方法
我們會看到,這個時候會去注冊類,這個類我們等流程到了在分析,我們先按啟動流程看過去;resolvePackagesToScan方法先獲取到需要掃描的包 ,然後再調用registerServiceBeans去注冊相關實例,我們重點來看下registerServiceBeans方法
接下來,我們主要去看下registerServiceBean方法
接下來,我們來看下buildServiceBeanDefinition方法
到這,ServiceBean注冊成功,ServiceBean類很重要,每個Dubbo service實例都對應一個ServiceBean,相關配置都在ServiceBean中;我們再回到開始注冊的類
類繼承了,實現了ApplicationListener,主要監聽了Spring容器生命周期,我們看下onApplicationContextEvent方法
我們可以看到,當Spring容器啟動成功時,會調用bboBootstrap.start();
接下來,主要邏輯在ServiceBean中,這個export方法在其父類ServiceConfig中,我們下一篇主要講ServiceConfig邏輯;