spring解析dubbo标签配置过程

399 阅读2分钟

这里debug的代码用的是github上dubbo项目的dubbo-demo里的dubbo-demo-xml下的代码。 spring的xml文件里自定义标签的解析是基于spring.handlers文件指定的解析类来解析的,dubbo标签是在dubbo-config-spring的jar包下的META-INF/spring.handlers文件里。这里先看下一个consumer端的配置文件内容:

<beans 
       // xmlns:xsi是xsi标签命名空间
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       // xmlns:dubbo是dubbo标签的命名空间
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       // 当前那xml文件默认的命名空间
       xmlns="http://www.springframework.org/schema/beans"
       // xsi:schemaLocation 配置了每个命名空间对应里配置规范,用来做格式校验
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <!--    当前应用名字,类似总的命名空间-->
    <dubbo:application name="demo-consumer"/>
   <!--    注册中心配置-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="6000"/>
    
    <!--    协议配置-->
    <dubbo:protocol name="dubbo"/>
    <!--    provider配置-->
    <bean id="providerService" class="org.apache.dubbo.demo.provider.ProviderServiceImpl"/>
    <dubbo:service interface="org.apache.dubbo.demo.provider.ProviderServiceImpl" ref="providerService" timeout="60000"/>
    <!--    consumer配置-->
    <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService" timeout="6000" />

</beans>

xmlns是指定命名空间,这里可以看到dubbo的命名空间配置是xmlns:dubbo="dubbo.apache.org/schema/dubb…"

1、根据dubbo标签的命名空间获取解析类

根据dubbo标签的命名空间找到META-INF/spring.handlers里的解析类: 在这里插入图片描述

这个匹配过程如图: 在这里插入图片描述

init()方法负责注册每个标签对应的具体Parser类:

public void init() {
        this.registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        this.registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        this.registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        this.registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
        this.registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
        this.registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        this.registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
        this.registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
        this.registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        this.registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        this.registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        // provider端配置
        this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        // consumer端配置
        this.registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        this.registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

可以看到这里包含了dubbo标签的所有种类的解析类,比如上面配置文件里的dubbo:application、dubbo:registry、dubbo:reference注解的解析器

2、将配置解析成Beanfinition并注册到BeanFactory过程

核心逻辑在DubboBeanDefinitionParser类的parse方法里,先看DubboBeanDefinitionParser类的构造器:

public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

beanClass就是每个配置所对应的类型,由上面的init()方法传入,比如dubbo:reference对应ReferenceBean.class parse方法代码如下,由于总的代码比较长,我这里只贴出主要代码:


  RootBeanDefinition beanDefinition = new RootBeanDefinition();
  // 设置类型,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象
  beanDefinition.setBeanClass(beanClass);
  ...
  
  //  将BeanDefinition注册到容器里,这个id会被作为beanName属性存在BeanDefinition里
  // registerBeanDefinition的方法签名是void registerBeanDefinition(String beanName, BeanDefinition beanDefinition),所以id是作为beanName传入的
			throws BeanDefinitionStoreException;
  if (StringUtils.isNotEmpty(id)) {
      parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
      beanDefinition.getPropertyValues().addPropertyValue("id", id);
  }

  ...
  
  // 遍历配置里的所有属性,比如dubbo:reference配置里的id、interface等属性,放到BeanDefinition里
  NamedNodeMap attributes = element.getAttributes();
    len = attributes.getLength();

    for(i = 0; i < len; ++i) {
        Node node = attributes.item(i);
        name = node.getLocalName();
        if (!props.contains(name)) {
            if (parameters == null) {
                parameters = new ManagedMap();
            }

            String value = node.getNodeValue();
            parameters.put(name, new TypedStringValue(value, String.class));
        }
    }

    if (parameters != null) {
        beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
    }
     

主要是三步:

  • 设置BeanDefinition的beanClass属性,beanClass由上面的init()方法传入,比如:dubbo:reference对应ReferenceBean.class,spring容器在实例化beanDefinition时,会反射这个类的构造器实例出bean对象
  • 通过parse方法的parserContext获取对应bean的BeanDefinitionRegistry,将BeanDefinition注册到BeanFactory里,这里有个关键点是传入的id会被作为beanName设置到BeanDefinition里,而在依赖注入时,会以beanName为key来找bean
  • 解析出配置的属性名字,比如consumer配置里的id、interface、group、version等信息,并将这些配置放到BeanDifinition的propertyValueList里(key为parameters),供后面实例化用。

到这里已经解析成BeanDefinition并注册到容器中,下一步就是Spring容器实例化BeanDefinition。后面会单独讲provider和consumer端怎么被实例化。