dubbo之xml配置

1:写在前面

这篇文章 介绍了如何使用spring的自定义标签来生成spring bean,好处是灵活可定制,dubbo提供的xml配置也使用了这种方式来完成配置,下面我们一起来看下。

2:定义

先来看下相关的xsd,namespacehandler的配置。

2.1:xsd

根据spring的规范xsd需要定义在META-INF/spring.schemas文件中,如下图:

在这里插入图片描述

内容如下:

http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd

其中的namespace是http\://code.alibabatech.com/schema/dubbo,xsd是META-INF/compat/dubbo.xsd。如下是示例xml命名空间和配置文件命名空间的对应关系:

在这里插入图片描述

如下是dubbo.xsd中定义的我们可以在xml配置文件中使用的配置项:

在这里插入图片描述

其中的element中的name就是我们在xml配置文件中使用的名称,如<xsd:element name="application" ...>,对应的就是\<dubbo:application\>。element中的type,就是元素可以定义的数据类型,如<xsd:element name="registry" type="registryType">对应的就是复杂类型registryType,配置了可以定义的元素,元素是否可选等信息,如下:

<xsd:complexType name="registryType">
    <!-- minOccurs="0"最小出现次数0,即可以不配置,也可以配置多次,配置多次可能有多个注册中心 -->        
    <xsd:sequence minOccurs="0" maxOccurs="unbounded">
        <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
    <!-- spring bean的唯一标识 -->        
    <xsd:attribute name="id" type="xsd:ID">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <!-- 注册中心的地址 -->        
    <xsd:attribute name="address" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The registry address. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <!-- 注册中心的端口 -->        
    <xsd:attribute name="port" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The registry default port. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <!-- 注册中心的协议,如zookeeper,nacos -->        
    <xsd:attribute name="protocol" type="xsd:string" use="optional">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The registry lookup protocol. ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:attribute>
    <!-- ... -->                                                       
</xsd:complexType>

2.2:namespacehandler

对应的文件如下图:

在这里插入图片描述

内容如下:

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

可以看到对应的namespacehandler实现类是com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,继承抽象类org.springframework.beans.factory.xml.NamespaceHandlerSupport,源码如下:

// com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        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 AnnotationBeanDefinitionParser());
    }

}

可以看到对各种标签提供了对应的DubboBeanDefinitionParser实例,负责将xml标签解析成bean definition,最终用于生成spring bean,这些类也正是本文要重点分析的内容,即如何解析xml配置文件为一个一个的bean定义。DubboBeanDefinitionParser实现了spring 定义的用于解析元素标签接口org.springframework.beans.factory.xml.BeanDefinitionParser,因此接下来我们重点要看的类就是DubboBeanDefinitionParser了。注册完毕后如下图:

在这里插入图片描述

3:DubboBeanDefinitionParser

3.1:构造方法

class FakeCls {
   // xml配置对应的类配置类
   private final Class<?> beanClass;
   // 当没有设置id属性时,是否自动生成,对于不会被其他其他配置引用的配置可设置为false,如<dubbo:reference>
   private final boolean required;

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

3.2:解析方法

对应的方法是com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parse,这是在spring的接口org.springframework.beans.factory.xml.BeanDefinitionParser中定义的方法,源码如下:

// com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parse
class FakeCls {
   @Override
   public BeanDefinition parse(Element element, ParserContext parserContext) {    
       return parse(element, parserContext, beanClass, required);
   }
}

继续看:

class FakeCls {
   private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
       // 创建bean定义,即返回的结果
       RootBeanDefinition beanDefinition = new RootBeanDefinition();
       // 设置要创建的spring bean类型
       beanDefinition.setBeanClass(beanClass);
       // 设置延迟加载,即只有在getBean获取,或者被注入到其他bean时才会创建,如果需要饥饿加载,可以配置<dubbo:reference ...init="true">            
       beanDefinition.setLazyInit(false);
       // 获取id属性,即spring bean名称
       String id = element.getAttribute("id");
       // 如果没有配置id属性,且要求自动生成
       if ((id == null || id.length() == 0) && required) {
           // 获取name属性,作为bean名称,这点和spring默认的规则是一致的,即优先使用id,然后再使用name        
           String generatedBeanName = element.getAttribute("name");
           // 如果是没有配置name属性            
           if (generatedBeanName == null || generatedBeanName.length() == 0) {
               // 如果是<dubbo:protocol>,则使用dubbo作为bean名称,因为全局只有一个,所以可以直接硬编码,当然后续也有避免重复的逻辑处理
               // 如果不是<dubbo:protocol>,则使用interface的值,即服务接口全限定名作为bean名称
               if (ProtocolConfig.class.equals(beanClass)) {
                   generatedBeanName = "dubbo";
               } else {
                   generatedBeanName = element.getAttribute("interface");
               }
           }
           // 依然没有获取到bean名称,则使用class的名称作为结果,如java.lang.Object.class结果就是"java.lang.Object"
           if (generatedBeanName == null || generatedBeanName.length() == 0) {
               generatedBeanName = beanClass.getName();
           }
           // 赋值到id属性,作为bean名称
           id = generatedBeanName;
           // 如果是id已经存在则通过添加自增序列的方式解决重复
           // 如当前id是xxx,存在的bean定义id是xxx,xxx2,xxx3,则这里最终的结果就是xxx->xxx4
           int counter = 2;
           while (parserContext.getRegistry().containsBeanDefinition(id)) {
               id = generatedBeanName + (counter++);
           }
       }
       // 成功获取了spring bean ID
       if (id != null && id.length() > 0) {
           // 已经包含了该spring bean ID的bean定义,理论上这里不会为true,因为前面已经做了重复处理了
           // 可能是为了健壮性才写了这里的代码吧
           if (parserContext.getRegistry().containsBeanDefinition(id)) {
               throw new IllegalStateException("Duplicate spring bean id " + id);
           }
           // 注册bean定义
           // 到这里bean id和bean定义的对应关系已经注册成功了,但此时bean定义还只是个空壳,内部信息还需要依赖后续程序进行初始化
           parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
           // 添加id属性到bean定义对象中
           beanDefinition.getPropertyValues().addPropertyValue("id", id);
       }
       // 2021-12-08 11:00:31
       if (ProtocolConfig.class.equals(beanClass)) {
           for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
               BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
               PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
               if (property != null) {
                   Object value = property.getValue();
                   if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                       definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                   }
               }
           }
       // 2021-12-08 14:20:21
       } else if (ServiceBean.class.equals(beanClass)) {
           String className = element.getAttribute("class");
           if (className != null && className.length() > 0) {
               RootBeanDefinition classDefinition = new RootBeanDefinition();
               classDefinition.setBeanClass(ReflectUtils.forName(className));
               classDefinition.setLazyInit(false);
               parseProperties(element.getChildNodes(), classDefinition);
               beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
           }
       // 2021-12-08 15:40:51,解析<dubbo:provider>的<dubbo:service>子标签们
       } else if (ProviderConfig.class.equals(beanClass)) {
           parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
       // 同2021-12-08 15:40:51,只不过这里是解析<dubbo:consumer>的<dubbo:reference>子标签们
       } else if (ConsumerConfig.class.equals(beanClass)) {
           parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
       }
       Set<String> props = new HashSet<String>();
       ManagedMap parameters = null;
       // 获取所有的方法,通过其写方法,即setter来为属性赋值
       // 个人认为这里变量直接命名为不太合适,因为不一定是写方法,叫mayBeSetter可能更合适一些
       for (Method setter : beanClass.getMethods()) {
           String name = setter.getName();
           // Modifier.isPublic(setter.getModifiers()):是public的 
           // setter.getParameterTypes().length == 1:参数的个数是1个
           if (name.length() > 3 && name.startsWith("set")
                   && Modifier.isPublic(setter.getModifiers())
                   && setter.getParameterTypes().length == 1) {
               // 获取参数的类型
               Class<?> type = setter.getParameterTypes()[0];
               // 获取属性的名称
               String propertyName = name.substring(3, 4).toLowerCase() + name.substring(4);
               // 为"-"分割的字符串,如setMyFirstGirlFriend->my-first-girl-friend
               String property = StringUtils.camelToSplitName(propertyName, "-");
               props.add(property);
               Method getter = null;
               //*** 验证是否有getter开始 ***//
               try {
                   getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
               } catch (NoSuchMethodException e) {
                   try {
                       getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
                   } catch (NoSuchMethodException e2) {
                   }
               }
               // !Modifier.isPublic(getter.getModifiers():getter不是public的
               // 或者!type.equals(getter.getReturnType()):和setter中的参数类型不一致,则忽略,继续下一个方法
               if (getter == null
                       || !Modifier.isPublic(getter.getModifiers())
                       || !type.equals(getter.getReturnType())) {
                   continue;
               }
               //*** 验证是否有getter结束 ***//
               // 2021-12-08 17:44:39
               // 如果属性名称是parameters,即当前方法是setParameters,则循环处理<dubbo:parameters>子标签设置属性
               if ("parameters".equals(property)) {
                   parameters = parseParameters(element.getChildNodes(), beanDefinition);
               //*** 属性为对象数据类型解析结束 ***//               
               // 2021-12-08 18:04:14
               // 处理<dubbo:method>子标签
               } else if ("methods".equals(property)) {
                   parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);
               // 2021-12-08 18:21:47
               } else if ("arguments".equals(property)) {
                   parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);
               //*** 属性为对象数据类型解析结束 ***//
               //*** 属性为String解析开始 ***//
               } else {
                   // 获取普通的属性值
                   String value = element.getAttribute(property);
                   if (value != null) {
                       value = value.trim();
                       if (value.length() > 0) {
                           // 配置service不注册到注册中心的情况,如配置:
                           /*
                            <dubbo:service interface="dongshi.daddy.service.RegistryNAService"
                                           ref="registryNAServiceImpl"
                                           registry="N/A"/>
                           */
                           if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {
                               // 因为设置了N/A,所以直接设置一个地址为N/A的RegistryConfig
                               RegistryConfig registryConfig = new RegistryConfig();
                               registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);
                               beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);
                           // 配置使用多个注册中心的情况,如下配置:
                           /*
                           <dubbo:registry id="registry2181" address="zookeeper://192.168.10.119:2181" />
                           <dubbo:registry id="registry2182" address="zookeeper://192.168.10.119:2182" />
                           <dubbo:service interface="dongshi.daddy.service.RegistryNAService"
                                           ref="registryNAServiceImpl"
                                           registry="registry2181,registry2182"/>
                           */
                           } else if ("registry".equals(property) && value.indexOf(',') != -1) {
                               // 2021-12-09 17:32:02
                               parseMultiRef("registries", value, beanDefinition, parserContext);
                           // 配置多个provider,不知道什么时候有这种配置,先忽略!!!
                           } else if ("provider".equals(property) && value.indexOf(',') != -1) {
                               parseMultiRef("providers", value, beanDefinition, parserContext);
                           // 多个协议配置,如配置:
                           /*
                           <dubbo:protocol id="protocol_1" name="dubbo" port="20826"/>
                           <dubbo:protocol id="protocol_2" name="dubbo" port="20827"/>
                           <dubbo:service interface="dongshi.daddy.service.RegistryNAService"
                                          ref="registryNAServiceImpl"
                                          registry="registry2181,registry2182" protocol="protocol_1,protocol_2"/>
                           */
                           // 这样就会使用2个协议暴露两个地址,如
                           /*
                           [zk: localhost:2181(CONNECTED) 52] ls /dubbo/dongshi.daddy.service.RegistryNAService/providers 
                           [dubbo://192.168.64.1:20827/..., dubbo://192.168.64.1:20826/...]
                           */
                           } else if ("protocol".equals(property) && value.indexOf(',') != -1) {
                               // 参考2021-12-09 17:32:02
                               parseMultiRef("protocols", value, beanDefinition, parserContext);
                           } else {
                               // 属性值
                               Object reference;
                               // 是基础数据类型,如name,host,port等
                               if (isPrimitive(type)) {
                                   // 向后兼容代码,主要是值的格式的转换
                                   if ("async".equals(property) && "false".equals(value)
                                           || "timeout".equals(property) && "0".equals(value)
                                           || "delay".equals(property) && "0".equals(value)
                                           || "version".equals(property) && "0.0.0".equals(value)
                                           || "stat".equals(property) && "-1".equals(value)
                                           || "reliable".equals(property) && "false".equals(value)) {
                                       // backward compatibility for the default value in old version's xsd
                                       value = null;
                                   }
                                   reference = value;
                               // protocol可以在很多标签中使用,比如<dubbo:service>,<dubbo:provider>,而其值可能是一个<dubbo:protocol>的spring bean id
                               // 也可能是一个协议的名称,此处就是处理这种缠绕的复杂情况的,即值有可能是一个bean ID,也有可能是协议对象,知道即可,先不深究
                               } else if ("protocol".equals(property)
                                       && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value)
                                       && (!parserContext.getRegistry().containsBeanDefinition(value)
                                       || !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName()))) {
                                   if ("dubbo:provider".equals(element.getTagName())) {
                                       logger.warn("Recommended replace <dubbo:provider protocol=\"" + value + "\" ... /> to <dubbo:protocol name=\"" + value + "\" ... />");
                                   }
                                   // backward compatibility
                                   ProtocolConfig protocol = new ProtocolConfig();
                                   protocol.setName(value);
                                   reference = protocol;
                               // *** 事件通知相关属性开始 *** //
                               // 2021-12-10 10:22:22
                               } else if ("onreturn".equals(property)) {
                                   int index = value.lastIndexOf(".");
                                   String returnRef = value.substring(0, index);
                                   String returnMethod = value.substring(index + 1);
                                   reference = new RuntimeBeanReference(returnRef);
                                   beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);
                               } else if ("onthrow".equals(property)) {
                                   int index = value.lastIndexOf(".");
                                   String throwRef = value.substring(0, index);
                                   String throwMethod = value.substring(index + 1);
                                   reference = new RuntimeBeanReference(throwRef);
                                   beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);
                               } else if ("oninvoke".equals(property)) {
                                   int index = value.lastIndexOf(".");
                                   String invokeRef = value.substring(0, index);
                                   String invokeRefMethod = value.substring(index + 1);
                                   reference = new RuntimeBeanReference(invokeRef);
                                   beanDefinition.getPropertyValues().addPropertyValue("oninvokeMethod", invokeRefMethod);
                               // *** 事件通知相关属性结束 *** //
                               } else {
                                   // ref引用其他spring bean的情况
                                   if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
                                       // 获取引用的bean定义
                                       BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                                       // 要求必须是单例,为什么???
                                       if (!refBean.isSingleton()) {
                                           throw new IllegalStateException("The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                                       }
                                   }
                                   reference = new RuntimeBeanReference(value);
                               }
                               // 添加值引用到当前标签的bean定义
                               beanDefinition.getPropertyValues().addPropertyValue(propertyName, reference);
                           }
                       }
                   }
               }
               //*** 属性为String解析结束 ***//
           }
       }
       // 解析类的属性的配置项,如<dubbo:service xxx="yyy">,在类中没有对应的setter,getter,则上述就解析不到,需要依靠这里的逻辑来解析
       // 可以认为不存在这种情况,即在标签中配置的属性,都是类属性,所以也可以忽略这里的逻辑
       NamedNodeMap attributes = element.getAttributes();
       int len = attributes.getLength();
       for (int i = 0; i < len; i++) {
           Node node = attributes.item(i);
           String 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);
       }
       // 返回标签对应的bean定义
       return beanDefinition;
   }
}

2021-12-08 11:00:31是处理\<dubbo:protocol\>的情况,详细参考3.2.1:dubbo:protocol2021-12-08 14:20:21处是处理\<dubbo:service\>,详细参考3.2.2:dubbo:service2021-12-08 15:40:51处是处理\<dubbo:provider\>中存在\<dubbo:service\>子标签的情况,如果是这种情况就不需要单独再定义dubbo:service了,如下可能配置:

<fakeRoot>
    <dubbo:application name="dongshidaddy-provider" owner="dongshidaddy"/>
    <dubbo:registry address="zookeeper://192.168.10.119:2181" />
    <dubbo:protocol id="dndndnddn" name="dubbo" port="20826"/>
    <bean class="dongshi.daddy.service.MyPathServiceImpl" id="myPathService"/>
    <dubbo:provider>
        <dubbo:service interface="dongshi.daddy.service.MyPathService" class="dongshi.daddy.service.MyPathServiceImpl" id="myPathServiceId" path="toMyPath">
            <property name="name" value="张三11"/>
            <property name="age" value="20"/>
        </dubbo:service>
    </dubbo:provider>
</fakeRoot>

详细参考3.2.3:dubbo:provider2021-12-08 17:44:39处是解析paramters子标签,获取parameters属性值,如下就是这种配置:

<fakeRoot>
    <dubbo:application name="dongshidaddy-provider" owner="dongshidaddy">
        <dubbo:parameter key="author" value="dongshidaddy"/>
        <dubbo:parameter key="writeYear" value="2021"/>
    </dubbo:application>
</fakeRoot>

源码如下:

class FakeCls {
    private static ManagedMap parseParameters(NodeList nodeList, RootBeanDefinition beanDefinition) {
        if (nodeList != null && nodeList.getLength() > 0) {
            ManagedMap parameters = null;
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node instanceof Element) {
                    // node.getLocalName():不带命名空间前缀的节点名称,这里如<dubbo:paramter>结果就是paramter
                    // 而node.getNodeName()的结果是dubbo:parameter
                    if ("parameter".equals(node.getNodeName())
                            || "parameter".equals(node.getLocalName())) {
                        if (parameters == null) {
                            parameters = new ManagedMap();
                        }
                        String key = ((Element) node).getAttribute("key");
                        String value = ((Element) node).getAttribute("value");
                        boolean hide = "true".equals(((Element) node).getAttribute("hide"));
                        if (hide) {
                            key = Constants.HIDE_KEY_PREFIX + key;
                        }
                        parameters.put(key, new TypedStringValue(value, String.class));
                    }
                }
            }
            return parameters;
        }
        return null;
    }
}

2021-12-08 18:04:14处是处理dubbo:method子标签,如下可能配置:

<fakeRoot>
   <dubbo:service
           interface="dongshi.daddy.service.ProviderService"
           ref="providerService">
       <dubbo:method name="SayHello" timeout="2000"/>
   </dubbo:service>
</fakeRoot>

源码如下:

class FakeClass {
    private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
                                         ParserContext parserContext) {
        if (nodeList != null && nodeList.getLength() > 0) {
            ManagedList methods = null;
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node instanceof Element) {
                    Element element = (Element) node;
                    if ("method".equals(node.getNodeName()) || "method".equals(node.getLocalName())) {
                        String methodName = element.getAttribute("name");
                        if (methodName == null || methodName.length() == 0) {
                            throw new IllegalStateException("<dubbo:method> name attribute == null");
                        }
                        if (methods == null) {
                            methods = new ManagedList();
                        }
                        // 递归调用生成<dubbo:method>的MethodConig类对应的beandefinition对象
                        BeanDefinition methodBeanDefinition = parse(((Element) node),
                                parserContext, MethodConfig.class, false);
                        // 设置方法对应的spring bean id
                        String name = id + "." + methodName;
                        BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(
                                methodBeanDefinition, name);
                        methods.add(methodBeanDefinitionHolder);
                    }
                }
            }
            // 添加方法信息到当前解析的标签对应的bean定义中
            if (methods != null) {
                beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
            }
        }
    }
}

2021-12-08 18:21:47处是处理dubbo:argument子标签,如配置:

<fakeRoot>
    <dubbo:service interface="dongshi.daddy.service.MyCallbackService" ref="callbackService" connections="1" callbacks="1000">
        <dubbo:method name="addListener">
            <dubbo:argument type="dongshi.daddy.service.CallbackListener" callback="true" />
        </dubbo:method>
    </dubbo:service>
</fakeRoot>

2021-12-09 17:32:02是处理引用了多个注册中心bean的情况,详细参考3.2.5:引用多个注册中心2021-12-10 10:22:22处是处理事件通知 相关属性,可能配置如下:

<fakeRoot>
    <dubbo:reference id="myEventNotifyServiceInConsumerSide"
                     interface="dongshi.daddy.service.MyEventNotifyService">
        <dubbo:method name="getUserName"
                      onreturn="consumerNotifyServiceImpl.onReturn"
                      oninvoke="consumerNotifyServiceImpl.onInvoke"
                      onthrow="consumerNotifyServiceImpl.onThrow"/>
    </dubbo:reference>
    <bean class="dongshi.daddy.service.eventynotify.ConsumerNotifyServiceImpl" id="consumerNotifyServiceImpl"/>
</fakeRoot>

格式是beanId.方法名称,代码解析都是通过.号分割后获取对应的spring bean ID和方法名称,然后存储到bean定义中。
详细参考3.2.4:parseArguments

3.2.1:dubbo:protocol

代码片段如下:

class FakeCls {
   void fakeMethod() {
      // 处理在<dubbo:service>中配置了protocol属性的情况,简单来说就是将自己设置到依赖于自己的bean定义中
      if (ProtocolConfig.class.equals(beanClass)) {
         for (String name : parserContext.getRegistry().getBeanDefinitionNames()) {
             // 获取bean定义并获取其protocol属性
             BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name);
             PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol");
             if (property != null) {
                 Object value = property.getValue();
                 if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) {
                     // 添加当前bean定义,其实就是<dubbo:service>对应的bean定义,protocol属性为当前的<dubbo:protocol>对应的bean定义
                     definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id));
                 }
             }
         }
      }
   }
}
3.2.2:dubbo:service

代码片段如下:

class FakeCls {
   void fakeMethod() {
      if (ProtocolConfig.class.equals(beanClass)) {
          // ...
      } else if (ServiceBean.class.equals(beanClass)) {
          // 获取class属性的值,配置的是服务类的实现类,如配置:<dubbo:service interface="xxx.MyService" class="xxx.MyServiceimpl" .../>
          // 也可配置ref属性来引用实现类对应的spring bean
          String className = element.getAttribute("class");
          if (className != null && className.length() > 0) {
              // 创建封装实现类的bean定义
              RootBeanDefinition classDefinition = new RootBeanDefinition();
              // 设置spring bean class
              classDefinition.setBeanClass(ReflectUtils.forName(className));
              // 设置为饥饿加载,即直接加载
              classDefinition.setLazyInit(false);
              // 2021-12-08 14:48:29
              // 解析<property>子标签,该子标签用于配置class的属性值,源码如下:
              parseProperties(element.getChildNodes(), classDefinition);
              // 添加ref属性,实现类的id默认使用service ID + ”Impl“,如service ID=myService,则结果就是myServiceImpl
              beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
          }
      }
   }
}

2021-12-08 14:48:29处是处理property子标签,即服务提供实现类的属性配置,源码如下:

class FakeCls {
   private static void parseProperties(NodeList nodeList, RootBeanDefinition beanDefinition) {
       if (nodeList != null && nodeList.getLength() > 0) {
           for (int i = 0; i < nodeList.getLength(); i++) {
               Node node = nodeList.item(i);
               if (node instanceof Element) {
                   if ("property".equals(node.getNodeName())
                           || "property".equals(node.getLocalName())) {
                       String name = ((Element) node).getAttribute("name");
                       if (name != null && name.length() > 0) {
                           String value = ((Element) node).getAttribute("value");
                           String ref = ((Element) node).getAttribute("ref");
                           if (value != null && value.length() > 0) {
                               beanDefinition.getPropertyValues().addPropertyValue(name, value);
                           } else if (ref != null && ref.length() > 0) {
                               beanDefinition.getPropertyValues().addPropertyValue(name, new RuntimeBeanReference(ref));
                           } else {
                               throw new UnsupportedOperationException("Unsupported <property name=\"" + name + "\"> sub tag, Only supported <property name=\"" + name + "\" ref=\"...\" /> or <property name=\"" + name + "\" value=\"...\" />");
                           }
                       }
                   }
               }
           }
       }
   }
}
3.2.3:dubbo:provider

代码片段如下:

class FakeCls {
    // element:要处理的元素
    // parserContext:解析上下文
    // beanClass:要解析的子标签对应的class
    // tag:要解析的子标签名称
    // property:当前元素的标签名
    // ref:当前要解析的标签对应的spring bean ID
    // beanDefinition:当前要解析的标签对应的beandefinition
    private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required, String tag, String property, String ref, BeanDefinition beanDefinition) {
        // 获取子节点集合
        NodeList nodeList = element.getChildNodes();
        if (nodeList != null && nodeList.getLength() > 0) {
            boolean first = true;
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                // 是Element才处理,忽略注释,空行等
                if (node instanceof Element) {
                    if (tag.equals(node.getNodeName())
                            || tag.equals(node.getLocalName())) {
                        // 处理default,不知道干啥用的,谁知道的话欢迎告知下!!!
                        if (first) {
                            first = false;
                            String isDefault = element.getAttribute("default");
                            if (isDefault == null || isDefault.length() == 0) {
                                beanDefinition.getPropertyValues().addPropertyValue("default", "false");
                            }
                        }
                        // 递归调用,获取tag子标签的bean定义对象
                        BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
                        // 设置当前子bean定义指向父bean定义
                        if (subDefinition != null && ref != null && ref.length() > 0) {
                            subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
                        }
                    }
                }
            }
        }
    }
}
3.2.4:parseArguments

如用在dubbo:method子标签中,设置方法的参数信息,源码如下:

class FakeCls {
    private static void parseArguments(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
                                           ParserContext parserContext) {
        if (nodeList != null && nodeList.getLength() > 0) {
            ManagedList arguments = null;
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                if (node instanceof Element) {
                    Element element = (Element) node;
                    if ("argument".equals(node.getNodeName()) || "argument".equals(node.getLocalName())) {
                        String argumentIndex = element.getAttribute("index");
                        if (arguments == null) {
                            arguments = new ManagedList();
                        }
                        // 创建dubbo:arguemnt对应的ArgumentConfig的beandefinition对象,并设置其id
                        BeanDefinition argumentBeanDefinition = parse(((Element) node),
                                parserContext, ArgumentConfig.class, false);
                        String name = id + "." + argumentIndex;
                        BeanDefinitionHolder argumentBeanDefinitionHolder = new BeanDefinitionHolder(
                                argumentBeanDefinition, name);
                        // 添加到结果集合中
                        arguments.add(argumentBeanDefinitionHolder);
                    }
                }
            }
            if (arguments != null) {
                // 添加到父bean定义中,如
                /*
                 <dubbo:method ...>
                    <dubbo:argument .../>
                    <dubbo:argument .../>
                    <dubbo:argument .../>
                 </dubbo:method>
                */
                // 就是添加到<dubbo:method>对应的beandefinition中
                beanDefinition.getPropertyValues().addPropertyValue("arguments", arguments);
            }
        }
    }
}
3.2.5:引用多个注册中心

源码如下:

// com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parseMultiRef
class FakeCls {
   // property:注册到bean定义中的属性键
   // value:引用的多个配置的值,逗号分隔
   // beanDefinition:当前解析的标签对应的bean定义
   // parserContext:解析上下文
   private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,
                                         ParserContext parserContext) {
       String[] values = value.split("\\s*[,]+\\s*");
       ManagedList list = null;
       // 循环添加到RuntimeBeanReference中,在后续流程中解析
       for (int i = 0; i < values.length; i++) {
           String v = values[i];
           if (v != null && v.length() > 0) {
               if (list == null) {
                   list = new ManagedList();
               }
               list.add(new RuntimeBeanReference(v));
           }
       }
       // 添加到bean定义中
       beanDefinition.getPropertyValues().addPropertyValue(property, list);
   }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值