Dubbo原理和源码解析之标签解析

github新增仓库 "dubbo-read"(点此查看),集合所有《Dubbo原理和源码解析》系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行分享。不定期更新,欢迎Follow。

一、Dubbo 配置方式

Dubbo 支持多种配置方式:

  • XML 配置:基于 Spring 的 Schema 和 XML 扩展机制实现
  • 属性配置:加载 classpath 根目录下的 dubbo.properties
  • API 配置:通过硬编码方式配置(不推荐使用)
  • 注解配置:通过注解方式配置(Dubbo-2.5.7及以上版本支持,不推荐使用)

对于 属性配置 方式,可以通过环境变量、-D 启动参数来指定 dubbo.properties 文件,加载文件顺序为:

    1. -D 启动参数
    2. 环境变量
    3. classpath 根目录

加载代码如下:

1public static final String DUBBO_PROPERTIES_KEY = "dubbo.properties.file"; 2public static final String DEFAULT_DUBBO_PROPERTIES = "dubbo.properties"; 3 4private static volatile Properties PROPERTIES; 5 6public static Properties getProperties() { 7 if (PROPERTIES == null) { 8 synchronized (ConfigUtils.class) { 9 if (PROPERTIES == null) { 10 String path = System.getProperty(Constants.DUBBO_PROPERTIES_KEY); 11 if (path == null || path.length() == 0) { 12 path = System.getenv(Constants.DUBBO_PROPERTIES_KEY); 13 if (path == null || path.length() == 0) { 14 path = Constants.DEFAULT_DUBBO_PROPERTIES; 15 } 16 } 17 PROPERTIES = ConfigUtils.loadProperties(path, false, true); 18 } 19 } 20 } 21 return PROPERTIES; 22}

本文主要分析 XML 配置的实现原理和源码,其他方式不予赘述。

二、XML 配置

文章开头已经提到,XML 配置方式是基于 Spring 的 Schema 和 XML 扩展机制实现的。通过该机制,我们可以编写自己的 Schema,并根据自定义的 Schema 自定义标签来配置 Bean。

使用 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 子模块下:

至于 XSD 的数据类型、如何定义,并不是本文的重点,请参考 W3school《Schema 教程》。

2.2 定义 JavaBean

dubbo-config-api 子模块中定义了 Dubbo 所有标签对应的 JavaBean,JavaBean 里面的属性一一对应标签的各配置项。

2.3 解析 Schema

Dubbo 服务框架的 Schema 的解析通过 DubboNamespaceHandler 和 DubboBeanDefinitionParser 实现。

其中,DubboNamespaceHandler 扩展了 Spring 的 NamespaceHandlerSupport,通过重写它的 init() 方法给各个标签注册对应的解析器:

1public class DubboNamespaceHandler extends NamespaceHandlerSupport { 2 static { 3 Version.checkDuplicate(DubboNamespaceHandler.class); 4 } 5 public void init() { 6 registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); 7 registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); 8 registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); 9 registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); 10 registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); 11 registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); 12 registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); 13 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); 14 registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); 15 registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); 16 } 17}

DubboBeanDefinitionParser 实现了 Spring 的 BeanDefinitionParser,通过重写 parse() 方法实现将标签解析为对应的 JavaBean:

1public class DubboBeanDefinitionParser implements BeanDefinitionParser { 2 public BeanDefinition parse(Element element, ParserContext parserContext) { 3 return parse(element, parserContext, beanClass, required); 4 } 5 6 @SuppressWarnings("unchecked") 7 private static BeanDefinition parse(Element element,ParserContext parserContext,Class<?> beanClass,boolean required) { 8 RootBeanDefinition beanDefinition = new RootBeanDefinition(); 9 beanDefinition.setBeanClass(beanClass); 10 beanDefinition.setLazyInit(false); 11 //......省略 12 if (ProtocolConfig.class.equals(beanClass)) { 13 for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { 14 BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); 15 PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); 16 if (property != null) { 17 Object value = property.getValue(); 18 if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) { 19 definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id)); 20 } 21 } 22 } 23 } else if (ServiceBean.class.equals(beanClass)) { 24 String className = element.getAttribute("class"); 25 if(className != null && className.length() > 0) { 26 RootBeanDefinition classDefinition = new RootBeanDefinition(); 27 classDefinition.setBeanClass(ReflectUtils.forName(className)); 28 classDefinition.setLazyInit(false); 29 parseProperties(element.getChildNodes(), classDefinition); 30 beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); 31 } 32 } else if (ProviderConfig.class.equals(beanClass)) { 33 parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition); 34 } else if (ConsumerConfig.class.equals(beanClass)) { 35 parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition); 36 } 37 //......省略 38 return beanDefinition; 39 } 40}

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.

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Dubbo原理和源码解析之标签解析 - HelloWorld