zoukankan      html  css  js  c++  java
  • 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(或者依赖解析)的时候进行实例化。

  • 相关阅读:
    机器学习(深度学习)
    机器学习(六)
    机器学习一-三
    Leetcode 90. 子集 II dfs
    Leetcode 83. 删除排序链表中的重复元素 链表操作
    《算法竞赛进阶指南》 第二章 Acwing 139. 回文子串的最大长度
    LeetCode 80. 删除有序数组中的重复项 II 双指针
    LeetCode 86 分割链表
    《算法竞赛进阶指南》 第二章 Acwing 138. 兔子与兔子 哈希
    《算法竞赛进阶指南》 第二章 Acwing 137. 雪花雪花雪花 哈希
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/8053867.html
Copyright © 2011-2022 走看看