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

  • 相关阅读:
    Vue整合nginx:(1)开发环境npm run dev下,通过nginx解决前后端分离造成的跨域问题
    SpringBoot 整合 ActiveMq
    SpringBoot 整合 slf4j 日志打印
    Java 抽象类的简单使用
    MongoDB基本使用
    node.js修改全局安装文件路径
    Vue + iview框架,搭建项目遇到的相关问题记录
    ThinkPHP重写路由,掩藏public/index.php
    thinkPhP + Apache + PHPstorm整合框架
    Ionic3,装饰器(@Input、@ViewChild)以及使用 Events 实现数据回调中的相关用法(五)
  • 原文地址:https://www.cnblogs.com/sunshine-2015/p/8053867.html
Copyright © 2011-2022 走看看