dubbo源码—dubbo自定义spring xml标签

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(或者依赖解析)的时候进行实例化。

时间: 2024-08-04 16:58:49

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

dubbo源码—dubbo简介

dubbo是一个RPC框架,应用方像使用本地service一样使用dubbo service.dubbo体系架构 上图中的角色: 最重要的是consumer.registry和provider consumer:服务调用者 provider:服务提供者 registry:供provider注册服务和consumer发现服务 monitor:监控调用过程的一些参数,比如:调用次数count container:容器,dubbo可以不依赖web容器工作 dubbo部署: registry(生产环境一

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

做dubbo的配置时很容易发现,dubbo有一套自己的标签,提供给开发者配置,其实每一个标签对应着一个 实体,在容器启动的时候,dubbo会对所有的配置进行解析然后将解析后的内容设置到实体里,最终dubbo会根据实体中的值生成贯穿全局的统一URL.利用自定义标签使配置简单明了化,与spring完美融合.下面自己写一个自定义标签,主要需要如下 几个步骤: 1.编写实体类2.编写Parser解析类3.编写NameSpaceHandle类4.配置spring.handlers5.配置spring.sc

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

在很多情况下,我们需要为系统提供可配置化支持,简单的做法可以直接基于Spring的标准Bean来配置,但配置较为复杂或者需要更多丰富控制的 时候,会显得非常笨拙.一般的做法会用原生态的方式去解析定义好的xml文件,然后转化为配置对象,这种方式当然可以解决所有问题,但实现起来比较繁琐, 特别是是在配置非常复杂的时候,解析工作是一个不得不考虑的负担.Spring提供了可扩展Schema的支持,这是一个不错的折中方案,完成一个自定义 配置一般需要以下步骤: 设计配置属性和JavaBean 编写XSD文

dubbo源码之一——xml schema扩展

dubbo源码版本:2.5.4 dubbo-parent |----dubbo-config |----dubbo-config-api |----com.alibaba.dubbo.config.* |----dubbo-config-spring |----com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler.java |----com.alibaba.dubbo.config.spring.schema.DubboBean

dubbo源码学习(四)初始化过程细节:解析服务

初学dubbo的源码,只做尝试性的去学习,做为自己学习的一个记录,各位看官如果觉得写的有错误或理解的不对,请在留言区告诉我,互相学习.本人能力有限,有大神进入 时请指点. 前面大概介绍了一下关于学习dubbo源码的一些基本知识,今天将真正去看dubbo内部的实现过程,看dubbo的源码前我先把dubbo的用户指南和开发指指南大概的看了一遍,然后从上面找到相应的切入点去看源码,今天将介绍的是dubbo的初始化解析bean的过程.从之前使用过dubbo一些经验,加上http://dubbo.io/的

【Dubbo源码阅读系列】之远程服务调用(上)

今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道远程服务具体的实现,消费者和提供者通过代理类来进行交互!! 一.JAVA 动态代理 简单看一段代码回顾一下动态代理: public class MyInvocationHandler implements InvocationHandler{ private Object object; publi

Dubbo源码分析系列-服务的发布

RPC简化类图 RPC模块核心接口和抽象实现 默认实现Dubbo协议的接口和抽象实现 服务发布过程 调用过程 上图是服务提供者暴露服务的主过程: 首先ServiceConfig类拿到对外提供服务的实际类ref(如:HelloWorldImpl),然后通过ProxyFactory类的getInvoker方法使用ref生成一个AbstractProxyInvoker实例,到这一步就完成具体服务到Invoker的转化.接下来就是Invoker转换到Exporter的过程. Dubbo处理服务暴露的关键

Dubbo 源码分析 - 服务导出全过程解析

1.服务导出过程 本篇文章,我们来研究一下 Dubbo 导出服务的过程.Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一是前置工作,主要用于检查参数,组装 URL.第二是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程.第三是向注册中心注册服务,用于服务发现.本篇文章将会对这三个部分代码进行详细的分析,在分析之前,我们先来了解一下服务的导出过程. Dubbo 支持两种服务导出方式,

dubbo源码分析6-telnet方式的管理实现

dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分析4-基于netty的dubbo协议的server dubbo源码分析5-dubbo的扩展点机制 dubbo提供了telnet的方式,直接用命令查看服务信息等.怎么实现的呢. 1. 编解码器 com.alibaba.dubbo.remoting.transport.netty.NettyCodecA