dubbo为了和spring更好的集成,提供了一些xml配置标签,也就是自定义标签

spring自定义标签

spring自定义标签的方式如下:

  1. 设计配置属性和JavaBean
  2. 编写xsd文件,校验xml属性和便于编辑器提示
  3. 编写NamespaceHandler和BeanDefinitionParser解析xml对应的标签
  4. 编写spring.handlers和spring.schemas串联起所有部件,放在META_INF下面
  5. 在xml中引入对应的标签就可以使用

dubbo自定义标签

dubbo对应的定义为:

  1. ApplicationConfig,ModuleConfig,RegistryConfig,MonitorConfig等
  2. META_INF/dubbo.xsd
  3. DubboNamespaceHandler,DubboBeanDefinitionParser
  4. META_INF/spring.handlers,META_INF/spring.schemas

spring在解析xml并加载bean定义的时候,会加载spring用来解析xml标签的handler,spring通过扫描所有类路径下的META_INF/spring.handlers来加载自定义标签的handler,紧接着会调用解析出的handler的init方法

// 类DefaultNamespaceHandlerResolver
public NamespaceHandler resolve(String namespaceUri) {
// 这里namespaceUri = http://code.alibabatech.com/schema/dubbo
// 加载所有的handlers配置,并取出dubbo自定义的handler
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
// ... 省略中间代码
// 实例化handler并缓存起来,然后调用init方法
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
// ... 省略中间代码
} private Map<String, Object> getHandlerMappings() {
// ... 省略中间代码
// 这里handlerMappingsLocation = META-INF/spring.handlers
// 加在classpath下所有spring.handlers
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
// ... 省略中间代码
}

在DubboNamespaceHandler#init方法中添加所有自定义标签对应的parser,将自定义的标签对应的parser添加到NamespaceHandlerSupport#parsers中

public void init() {
// 参数true表示在解析的时候如果没有指定id需要生成一个id
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解析,每一个自定义标签对应一个JavaBean(前面在spring自定义标签的方法中提到),在解析到自定义标签的时候根据标签名称获取到parser解析标签,调用parser.parse方法,parse方法主要作用就是解析标签构造对应JavaBean的RootBeanDefinition,构造好后注入spring容器中

private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
String id = element.getAttribute("id");
if ((id == null || id.length() == 0) && required) {
// 如果没有配置id,先依次试图获取name、interface作为generatedBeanName
if (generatedBeanName == null || generatedBeanName.length() == 0) {
// 如果上面的属性都没有配置则直接获取class的全名作为id
generatedBeanName = beanClass.getName();
}
// 以防该class有其他的实例(比如可以配置多协议,dubbo:protocol),在后面加上一个数字加以区分
id = generatedBeanName;
int counter = 2;
while(parserContext.getRegistry().containsBeanDefinition(id)) {
id = generatedBeanName + (counter ++);
}
}
if (id != null && id.length() > 0) {
// double check
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
// 将BeanDefinition注册到容器
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
if (ProtocolConfig.class.equals(beanClass)) {
// ... 省略中间代码
// 如果是ProtocolConfig,会找出容器中所有配置了protocol属性的bean,并设置为RuntimeBeanReference
definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
} else if (ServiceBean.class.equals(beanClass)) {
// ... 省略中间代码
// 如果dubbo:service配置class属性,将ref属性设置为class属性对应的bean
beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
} else if (ProviderConfig.class.equals(beanClass)) {
// 解析dubbo:provider包含的子标签
parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
} else if (ConsumerConfig.class.equals(beanClass)) {
// 解析dubbo:consumer包含的子标签
parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
// ... 省略中间代码
// 下面的代码就是解析bean属性(比如:parameters、registry等)添加到beanDefinition中
}

总结

dubbo自定义标签都是需要实例化的bean,所以dubbo解析自定义标签的主要工作就是讲bean定义注册到容器中,等getBean(或者依赖解析)的时候进行实例化。

dubbo源码—dubbo自定义spring xml标签的更多相关文章

  1. Dubbo源码-Dubbo是如何随心所欲自定义XML标签的

    叨叨 今天考虑了很久要不要写这篇文章. 距离<Dubbo源码>系列的开篇到现在已经快两个月时间了.当时是想着工作上的RPC框架使用存在一些让人头疼的问题,就来看看Dubbo给出了一套什么样 ...

  2. dubbo源码分析(一)-从xml到我们认识的Java对象

    项目中用的dubbo的挺多的,然后随着自己对dubbo的慢慢深入,自己也希望能够了解dubbo的底层实现,这半年来一直在看dubbo的源码,有点断断续续的,于是准备写一个dubbo源码系列的分析文章, ...

  3. dubbo源码—dubbo简介

    dubbo是一个RPC框架,应用方像使用本地service一样使用dubbo service.dubbo体系架构 上图中的角色: 最重要的是consumer.registry和provider con ...

  4. dubbo源码学习(二) : spring 自定义标签

    做dubbo的配置时很容易发现,dubbo有一套自己的标签,提供给开发者配置,其实每一个标签对应着一个 实体,在容器启动的时候,dubbo会对所有的配置进行解析然后将解析后的内容设置到实体里,最终du ...

  5. spring源码学习之:xml标签扩展配置例子

    在很多情况下,我们需要为系统提供可配置化支持,简单的做法可以直接基于Spring的标准Bean来配置,但配置较为复杂或者需要更多丰富控制的 时候,会显得非常笨拙.一般的做法会用原生态的方式去解析定义好 ...

  6. 1、Dubbo源码解析--Dubbo如何驱动Spring IOC容器并配合工作的?

    首先Spring要注入自己的bean需要在Spring-provider.xml(提供者spring注入文件,名字可能不一样)添加bean注入,其中有dubbo的自定义标签,xml如何识别这些标签?拿 ...

  7. dubbo源码之一——xml schema扩展

    dubbo源码版本:2.5.4 dubbo-parent |----dubbo-config |----dubbo-config-api |----com.alibaba.dubbo.config.* ...

  8. 【Dubbo源码阅读系列】服务暴露之本地暴露

    在上一篇文章中我们介绍 Dubbo 自定义标签解析相关内容,其中我们自定义的 XML 标签 <dubbo:service /> 会被解析为 ServiceBean 对象(传送门:Dubbo ...

  9. Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)

    前面讲过Dubbo SPI拓展机制,通过ExtensionLoader实现可插拔加载拓展,本节将接着分析Dubbo的服务发布过程. 以源码中dubbo-demo模块作为切入口一步步走进Dubbo源码. ...

随机推荐

  1. YbSoftwareFactory 代码生成插件【十三】:Web API 的安全性

    ASP.NET Web API 可非常方便地创建基于 HTTP 的 Services,这些服务可以非常方便地被几乎任何形式的平台和客户端(如浏览器.Windows客户端.Android设备.IOS等) ...

  2. 作业七:团队项目——Alpha版本冲刺阶段-12

    今日编写代码如下: class ChessRule { /**卒子的移动规则*/ public void armsRule(int Man,JLabel play,MouseEvent me){ // ...

  3. Git Day02,工作区,暂存区,回退,删除文件

    1st,工作区回退:2st,暂存区回退:3rd,删除文件:

  4. 虚拟机设置静态ip

    最近学习hadoop,用到虚拟机来做分布式,由于hadoop要配置slave节点的主机名,所以需要修改hosts文件的ip地址和主机名的映射关系. 但是虚拟机每次重启后,ip地址都会变 ,这样每次都得 ...

  5. echart.js的使用与API

    ---恢复内容开始--- echart.js的使用与API 1.echart.js的使用: 第一步:在head标签或body下创建一个script标签去引用echart.js,(该文件可以在echar ...

  6. Google Chrome七大新特性

    Google Chrome 在日常生活中扮演的角色不只是一个功能强大的网络浏览器,它内置的 DevTools 同样也是网络开发者进行网络开发的重要工具. DevTools 在不断的进行版本更新,其中有 ...

  7. 【转】coco2d-x 纹理研究

    1.通常情况下用PVR格式的文件来进行图片显示的时候,在运行速度和内存消耗方面都要比PNG格式要快和小.一般情况下PVR消耗的内存比PNG消耗的内存小25%左右.PVR格式可以用ZWoptex导出.P ...

  8. C/C++:多个.cpp文件包括同一个.h头文件定义方法

    本文解决multiple definition of `XX'的错误.[出于反爬虫的目的,你不是在http://blog.csdn.net/zhanh1218上看到的,肯定不是最新最全的.] 关于头文 ...

  9. 今天我得鼓吹一波 Kotlin

    Kotlin 被作为 Google 官方语言也有一年多了,但除了刚宣布那个月极度火爆以外,后面生活又回归了平静.不少小伙伴紧跟 Google 爸爸的步伐,也对 Kotlin 有了或多或少的了解,Git ...

  10. java中垃圾回收算法讲解

      判断对象是否存活的方法: 一.引用计数算法(Reference Counting) 介绍:给对象添加一个引用计数器,每当一个地方引用它时,数据器加1:当引用失效时,计数器减1:计数器为0的即可被回 ...