Dubbo原理和源碼解析之標籤解析

github新增倉庫 "dubbo-read"(點此查看),集合全部《Dubbo原理和源碼解析》系列文章,後續將繼續補充該系列,同時將針對Dubbo所作的功能擴展也進行分享。不按期更新,歡迎Follow。

 

1、Dubbo 配置方式

Dubbo 支持多種配置方式:git

  • XML 配置:基於 Spring 的 Schema 和 XML 擴展機制實現
  • 屬性配置:加載 classpath 根目錄下的 dubbo.properties
  • API 配置:經過硬編碼方式配置(不推薦使用)
  • 註解配置:經過註解方式配置(Dubbo-2.5.7及以上版本支持,不推薦使用)

對於 屬性配置 方式,能夠經過環境變量、-D 啓動參數來指定 dubbo.properties 文件,加載文件順序爲:github

    1. -D 啓動參數
    2. 環境變量
    3. classpath 根目錄

加載代碼以下:spring

public static final String DUBBO_PROPERTIES_KEY = "dubbo.properties.file"; public static final String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties"; private static volatile Properties PROPERTIES; public static Properties getProperties() { if (PROPERTIES == null) { synchronized (ConfigUtils.class) { if (PROPERTIES == null) { String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY); if (path == null || path.length() == 0) { path = System.getenv(Constants.DUBBO_PROPERTIES_KEY); if (path == null || path.length() == 0) { path = Constants.DEFAULT_DUBBO_PROPERTIES; } } PROPERTIES = ConfigUtils.loadProperties(path, false, true); } } } return PROPERTIES; }

本文主要分析 XML 配置的實現原理和源碼,其餘方式不予贅述。api

2、XML 配置

文章開頭已經提到,XML 配置方式是基於 Spring 的 Schema 和 XML 擴展機制實現的。經過該機制,咱們能夠編寫本身的 Schema,並根據自定義的 Schema 自定義標籤來配置 Bean。app

使用 Spring 的 XML 擴展機制有如下幾個步驟:框架

    1. 定義 Schema(編寫 .xsd 文件)
    2. 定義 JavaBean
    3. 編寫 NamespaceHandler 和 BeanDefinitionParser 完成 Schema 解析
    4. 編寫 spring.handlers 和 spring.schemas 文件串聯解析部件
    5. 在 XML 文件中應用配置
Dubbo 配置相關的代碼在 dubbo-config 模塊。

2.1 定義 Schema

Schema 的定義體如今 .xsd 文件上,文件位於 dubbo-config-spring 子模塊下:ide

至於 XSD 的數據類型、如何定義,並非本文的重點,請參考 W3school《Schema 教程》。ui

2.2 定義 JavaBean

dubbo-config-api 子模塊中定義了 Dubbo 全部標籤對應的 JavaBean,JavaBean 裏面的屬性一一對應標籤的各配置項。編碼

2.3 解析 Schema

Dubbo 服務框架的 Schema 的解析經過 DubboNamespaceHandler 和 DubboBeanDefinitionParser 實現。url

其中,DubboNamespaceHandler 擴展了 Spring 的 NamespaceHandlerSupport,經過重寫它的 init() 方法給各個標籤註冊對應的解析器:

public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }

DubboBeanDefinitionParser 實現了 Spring 的 BeanDefinitionParser,經過重寫 parse() 方法實現將標籤解析爲對應的 JavaBean:

public class DubboBeanDefinitionParser implements BeanDefinitionParser { public BeanDefinition parse(Element element, ParserContext parserContext) { return parse(element, parserContext, beanClass, required); } @SuppressWarnings("unchecked") private static BeanDefinition parse(Element element,ParserContext parserContext,Class<?> beanClass,boolean required) { RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(beanClass); beanDefinition.setLazyInit(false); //......省略
        if (ProtocolConfig.class.equals(beanClass)) { for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); if (property != null) { Object value = property.getValue(); if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) { definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id)); } } } } else if (ServiceBean.class.equals(beanClass)) { String className = element.getAttribute("class"); if(className != null && className.length() > 0) { RootBeanDefinition classDefinition = new RootBeanDefinition(); classDefinition.setBeanClass(ReflectUtils.forName(className)); classDefinition.setLazyInit(false); parseProperties(element.getChildNodes(), classDefinition); beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); } } else if (ProviderConfig.class.equals(beanClass)) { parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition); } else if (ConsumerConfig.class.equals(beanClass)) { parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition); } //......省略
        return beanDefinition; } }

2.4 串聯部件

上面咱們已經知道解析的實現類了,那麼 Spring 又如何知道該用 DubboNamespaceHandler 來解析 Dubbo 標籤呢?這經過編寫 spring.handlers 文件實現。

spring.handlers 內容以下:

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

而後,Spring 經過 spring.schemas 文件得知 Dubbo 標籤的 Schema 是 dubbo.xsd,並以此校驗應用 XML 配置文件的格式。

spring.schemas 文件內容以下:

http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

文件位置以下:

2.5 應用配置

經過以上步驟,Dubbo 服務框架就完成了標籤解析的功能,用戶在應用程序中按照 dubbo.xsd 的格式配置 XML 便可。

Over.

相關文章
相關標籤/搜索