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
-
- -D 啓動參數
- 環境變量
- 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 擴展機制有如下幾個步驟:框架
-
- 定義 Schema(編寫 .xsd 文件)
- 定義 JavaBean
- 編寫 NamespaceHandler 和 BeanDefinitionParser 完成 Schema 解析
- 編寫 spring.handlers 和 spring.schemas 文件串聯解析部件
- 在 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.