Dubbo3 源碼系列 Dubbo“糾葛”(入門篇)

来源:https://www.cnblogs.com/weir110/archive/2022/06/03/16340190.html
-Advertisement-
Play Games

來源:my.oschina.net/xiaolyuh/blog/1615639 在日常開發中有很多地方都有類似扣減庫存的操作,比如電商系統中的商品庫存,抽獎系統中的獎品庫存等。 解決方案 使用mysql資料庫,使用一個欄位來存儲庫存,每次扣減庫存去更新這個欄位。 還是使用資料庫,但是將庫存分層多份存 ...


日期 更新說明
2022年5月28日 spring xml部分解讀
2022年6月3日 spring annotation部分解讀
人生不相見, 動如參與商。
今夕復何夕, 共此燈燭光。
少壯能幾時, 鬢髮各已蒼。
訪舊半為鬼, 驚呼熱中腸。
焉知二十載, 重上君子堂。
昔別君未婚, 兒女忽成行。
怡然敬父執, 問我來何方。
問答未及已, 兒女羅酒漿。
夜雨剪春韭, 新炊間黃粱。
主稱會面難, 一舉累十觴。
十觴亦不醉, 感子故意長。
明日隔山嶽, 世事兩茫茫。
杜甫 《贈衛八處士》摘選(文章加粗部分;此情此景覺得有點共鳴哈)分享 一下。
本來計劃Dubbo3的源碼解讀系列,打算兩周更新一篇計劃;很抱歉第三篇來晚了;可能標題寫的有點過於情緒化;勿噴“標題黨”,相信我絕對是筆者內容決定認真原創。

前言
上篇文章《Dubbo3 源碼系列 -- 環境準備》啟示已經介紹了,Dubbo API的方式使用了,可能對於多數使用者,使用Spring集成Dubbo的 xml 和 註解形式可能更多;那麼這篇文章從Spring如何集成Spring的支持角度開始解讀。
開始之前,源碼基於目前Dubbo3.0.7版本;解讀可能是目前文檔源碼有些出入,請註意版本。
Dubbo和Spring部分
Dubbo支持Spring的xml方式
基本使用和案例
其他書籍或者教程;通常會為你編寫一個,HelloWorld的案例;筆者覺得,看這篇文章的讀者的水平應該是達到了這個級別的;不在手寫Dubbo的入門案例;Dubbo官方源碼 dubbo-demo提供好了源碼案例。

  1. 服務提供者編寫:

  2. 服務消費者編寫:

  3. 服務提供者、消費者主類
    (篇幅問題;提供鏈接)
    provider/Application
    consumer/Application
    總結:
    通知配置Dubbo標簽,Spring容器啟動時,載入Dubbo相關的Bean,同時生成對應的介面代理,為RPC調用做好準備。
    思考:(源碼閱讀需要解決問題)

  4. Dubbo如何支持Spring的?

  5. Dubbo通過代理Bean,實現Spring註入和RPC調用,那麼註入哪些Bean,具體細節如何呢?
    有了問題,讓我們帶著問題點閱讀源碼可能效果更好。
    源碼解讀:
    Dubbo在利用Spring可擴展的XML Schema機制時,在Spring啟動時載入Dubbo自定義的標簽內容(Dubbo的ximl文檔[官方文檔]);Dubbo的標簽較多,這裡依照 dubbo:service 配置(dubbo-service文檔)為例;可以大體熟悉一下基本用法。
    dubbo:service
    dubbo:service 配置
    服務提供者暴露服務配置。對應的配置類:org.apache.dubbo.config.ServiceConfig
    屬性 對應URL參數 類型 是否必填 預設值 作用 描述 相容性
    interface class 必填 服務發現 服務介面名 1.0.0以上版本
    ref object 必填 服務發現 服務對象實現引用
    Spring可擴展的XML Schema機制

創建一個 XML Schema 文件,描述自定義的合法構建模塊,也就是xsd文件
dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
自定義個處理器類,並實現NamespaceHandler介面(比較容易)

  1. 自定義介面handler實現;懂點實現 parse介面:
    ● init(): NamespaceHandler被使用之前調用,完成NamespaceHandler的初始化
    ● BeanDefinition parse(Element, ParserContext): 當遇到頂層元素時被調用
    ● BeanDefinition decorate(Node,BeanDefinitionHandler,ParserContext): 當遇到一個屬性或者嵌套元素的時候調用
    @Override
    public void init() {
    // spring在解析Dubbo.xml標簽時,調用模板init方法初始化用於解析Dubbo自定義的XML標簽註解的解析器(DubboBeanDefinitionParser)
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class));
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class));
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class));
    registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class));
    registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class));
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class));
    registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class));
    registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class));
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class));
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class));
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class));
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class));
    registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

/**
* Override {@link NamespaceHandlerSupport#parse(Element, ParserContext)} method
*
* @param element {@link Element}
* @param parserContext {@link ParserContext}
* @return
* @since 2.7.5
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
registerAnnotationConfigProcessors(registry);

// initialize dubbo beans
DubboSpringInitializer.initialize(parserContext.getRegistry());

/**
     * 重點調用Spring的org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse方法;
     * 就是對應init內部方法的DubboBeanDefinitionParser{@link DubboBeanDefinitionParser#parse(Element, ParserContext)}#parse方法
 */
BeanDefinition beanDefinition = super.parse(element, parserContext);
setSource(beanDefinition);
return beanDefinition;

}
2. 自定義BeanDefinitionParser實現NamespaceHandlerSupport介面;主要解析對應的BeanDefination
org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser;有興趣自行分析
3. 註冊handler和schema
為了讓Spring在解析xml的時候能夠感知到我們的自定義元素,我們需要把namespaceHandler和xsd文件放到2個指定的配置文件中,這2個文件都位於META-INF目錄中
Spring通過XML解析程式將其解析為DOM樹,通過NamespaceHandler指定對應的Namespace的BeanDefinitionParser將其轉換成BeanDefinition。再通過Spring自身的功能對BeanDefinition實例化對象。
在期間,Spring還會載入兩項資料:
○ META-INF/spring.handlers
指定NamespaceHandler(實現org.springframework.beans.factory.xml.NamespaceHandler)介面,或使用org.springframework.beans.factory.xml.NamespaceHandlerSupport的子類。
○ META-INF/spring.schemas
在解析XML文件時將XSD重定向到本地文件,避免在解析XML文件時需要上網下載XSD文件。通過現實org.xml.sax.EntityResolver介面來實現該功能
4. 對於註冊Context容器;這裡是Spring自定義機制;目前由於篇幅問題;多做過多的細節介紹:(請看圖)
Spring Bean 載入流程分析(通過 XML 方式載入)
Spring-ClassPathXmlApplicationContext源碼探討-解析XML文件_半笙彷徨的博客-CSDN博客
Spring 的annotation部分源碼
基本使用和案例

服務介面

服務提供者:

源碼解讀
使用註解需要重點關註:@PropertySource、@DubboService、@DubboReference 就可以了
思考:

  1. Dubbo的註解都幹了些啥?(這個基本看下註釋和源碼基本都可以明白)
  2. Dubbo如何如何讓Spring識別Dubbo自定義的註解呢?
  3. 還記得之前註解對應的properties文件嗎?這個也作為小問題點
    源碼入口 -- @EnableDubbo
    ● @EnableDubboConfig :載入Dubbo相關配置
    需要配置多個對象(ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等),而這個註解的目的就是幫我們簡化這些配置,只要將相應的配置項通過.properties文件交由@PropertySource註解由Spring載入到Environment中,Dubbo即可通過Environment中相應的屬性與ApplicationConfig對象同名的屬性進行綁定,而這個過程都是自動。
    ● @DubboComponentScan:掃描類路徑以獲取將自動註冊為Spring bean的註釋組件。

@EnableDubboConfig
@Import(DubboConfigConfigurationRegistrar.class)註解導入;用於解析和載入通用的Bean :
// ImportBeanDefinitionRegistrar 註解 配合 @Import使用
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // initialize dubbo beans(Spring從初始化,載入Dubbo相關的Bean)
    DubboSpringInitializer.initialize(registry);

    // Config beans creating from props have move to ConfigManager

// AnnotationAttributes attributes = AnnotationAttributes.fromMap(
// importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
//
// boolean multiple = attributes.getBoolean("multiple");
//
// // Single Config Bindings
// registerBeans(registry, DubboConfigConfiguration.Single.class);
//
// if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
// registerBeans(registry, DubboConfigConfiguration.Multiple.class);
// }

}

}
DubboBeanUtils#registerCommonBeans
/**
* Register the common beans
*
* @param registry {@link BeanDefinitionRegistry}
* @see ReferenceAnnotationBeanPostProcessor
* @see DubboConfigDefaultPropertyValueBeanPostProcessor
* @see DubboConfigAliasPostProcessor
* @see DubboBootstrapApplicationListener
*/
static void registerCommonBeans(BeanDefinitionRegistry registry) {

    registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);

    registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);

    // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
    registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
        ReferenceAnnotationBeanPostProcessor.class);

    // TODO Whether DubboConfigAliasPostProcessor can be removed ?
    // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
    registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
        DubboConfigAliasPostProcessor.class);

    // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean

// registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
// DubboBootstrapApplicationListener.class);

    // register ApplicationListeners
    registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
    registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);

    // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
    registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
        DubboConfigDefaultPropertyValueBeanPostProcessor.class);

    // Dubbo config initializer
    registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);

    // register infra bean if not exists later
    registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}

總結:
使用 @EnableDubbo載入Dubbo對應的共有配置Bean和postprocesser,掃描註解,載入@Service和@reference註解。更多內容啟示關於Spring的框架的底層內容熟悉,Dubbo只是靈活的使用Spring的API。
幫助文檔
xsd-custom-registration
Spring中的XML schema擴展機制
Spring容器啟動流程+Bean的生命周期【附源碼】 - 天喬巴夏丶 - 博客園 (cnblogs.com)
緣起 Dubbo ,講講 Spring XML Schema 擴展機制 - 掘金 (juejin.cn)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有片很早以前買的Nokia5110 LCD一直在紙箱里吃灰. 可能是買其它配件時送的? 沒有合適的用途一直扔在那裡, 偶爾見到總會想什麼時候有空給它點一下. 外觀長這樣. 其實市面上這種模塊有幾個不同的版本, 另一種版本是上下有兩排排針, 方便不同的PCB佈局, 功能是一樣的. 單色LCD, 84x... ...
  • 一、PL/SQL運算符 類型 符號 說明 賦值運算符 := Java 和 C# 中都是等號,PL/SQL 的賦值是 := 特殊字元 || 字元串連接操作符 -- PL/SQL 中的單行註釋 /*,*/ PL/SQL 中的多行註釋,多行註釋不能嵌套。 <<,>> 標簽分隔符,只為了標識程式特殊位置。 ...
  • 非常感謝小趙同學給我反饋的這個 Bug
  • <!-- JS代碼的書寫位置 1、可以將JS代碼書寫在script標簽中 2、可以將JS代碼寫在標簽的onclick屬性中,當我們點擊按鈕時,JS代碼才會執行 3、可以將JS代碼寫在超鏈接的href屬性中,這些點擊超鏈接時,會執行JS代碼 --> tips: 1、可以在超鏈接的href屬性中寫上空的 ...
  • 前言 在平常的後端項目開發中,狀態機模式的使用其實沒有大家想象中那麼常見,筆者之前由於不在電商領域工作,很少在業務代碼中用狀態機來管理各種狀態,一般都是手動get/set狀態值。去年筆者進入了電商領域從事後端開發。電商領域,狀態又多又複雜,如果仍然在業務代碼中東一塊西一塊維護狀態值,很容易陷入出了問 ...
  • 註解 前言 以前學習到「註解」的時候,沒有好好理解註解是如何工作的,只是知道註解可以實現一些功能,總而言之,就是懵懵懂懂。 不過,即使你不知道什麼是註解,但肯定接觸過註解,比如方法的重寫,在方法上面寫著 @Override,這個東西就是註解。 好了,下麵就開始回爐重造!打好基礎! 什麼是註解? 註解 ...
  • 1. Spring IOC源碼研究筆記(2)——ApplicationContext系列 1.1. 繼承關係 非web環境下,一般來說常用的就兩類ApplicationContext: 配置形式為XML的:ClassPathXmlApplicationContext、FileSystemXmlApp ...
  • C++異常處理(較淺) 基本概念 異常處理,是編程語言或電腦硬體里的一種機制,用於處理軟體或信息系統中出現異常的情況,保證程式運行的穩定性和健壯性,防止程式崩潰。但是過渡使用會影響程式運行的效率。 把可能出現的問題放到try里,進行監控 throw前面的語句如果滿足,就拋出**(下麵的語句就不執行 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...