文章

XML配置

XML配置

本文基于 Dubbo 2.6.1 版本,望知悉。

友情提示,【配置】这块的内容,会相对比较枯燥。所以,如果看到一些很难懂的地方,建议先跳过。

对于 Dubbo ,重点是要去理解,多协议、RPC、容错等等模块,而不是【配置】。

估计好多胖友被【配置】这章劝退了把???

1. 概述

在 Dubbo 提供的几种方式中,XML 配置肯定是大家最熟悉的方式。

如果胖友不熟悉,可以查看如下文档:

XML 配置,自定义 <dubbo: /> 标签,基于 Spring XML 进行解析。如果不了解的胖友,可以查看如下文档:

2. 定义

2.1 sprng.schemas

Dubbo 在 dubbo-spring-config 的 META-INF/spring.schemas 定义如下:

```plain text http://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- xmlns
为
http://code.alibabatech.com/schema/dubbo
- xsd
为
META-INF/dubbo.xsd

## 2.2 dubbo.xsd

[dubbo.xsd](https://github.com/YunaiV/dubbo/blob/05dd4fe091e37d08e01d4e243e3fc24dbfdc61ff/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd) 定义如下:

![](/assets/images/learning/dubbo/dubbo-configuration-xml/181c485d9612fc254d4c5f52c6f381a7.png)

dubbo.xsd

- **元素的名称**
,定义了
。例如,
对应
。
- **内建数据类型的名称**
,定义了
。例如,
对应
。
- **复杂类型**
,定义了
。例如
如下:

![](/assets/images/learning/dubbo/dubbo-configuration-xml/8b59003d74a3ef0f4dae42ef7ed2d961.png)

applicationType

## 2.3 spring.handlers

[spring.handlers](https://github.com/YunaiV/dubbo/blob/05dd4fe091e37d08e01d4e243e3fc24dbfdc61ff/dubbo-config/dubbo-config-spring/src/main/resources/META-INF/spring.handlers) 定义如下:

```plain text
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
  • 定义了 Dubbo 的 XML Namespace 的处理器 DubboNamespaceHandler 。

2.4 DubboNamespaceHandler

com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler ,实现 org.springframework.beans.factory.xml.NamespaceHandlerSupport抽象类,Dubbo 的 XML Namespace 的处理器。

#init() 方法,定义了每个 对应的 org.springframework.beans.factory.xml.BeanDefinitionParser ,代码如下:

1
@Overridepublic 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()); // 废弃}
  • 细心的胖友,会看到 初始化解析 service 标签使用的是 ServiceBean ,而不是 ServiceConfig , reference 表示用的是 ReferenceBean ,因为无论是 ServiceConfig 还是 ReferenceBean ,在解析完具体配置后,需要调用它们对应的方法进行 。 考虑到篇幅,我们在后续文章分享。这篇我们重点放在 。

3. 解析

com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser ,实现 org.springframework.beans.factory.xml.BeanDefinitionParser接口,Dubbo Bean 定义解析器。

3.1 构造方法

构造方法 ,代码如下:

1
/** * Bean 对象的类 */private final Class<?> beanClass;/** * 是否需要 Bean 的 `id` 属性 */private final boolean required;public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {    this.beanClass = beanClass;    this.required = required;}
  • beanClass ,Bean 对象的 。
  • required自动生成编号 ,是否需要在 Bean 对象的编号( id ) 不存在时, 。无需被其他应用引用的配置对象,无需自动生成编号。例如有 。

3.2 解析方法【主流程】

#parse(Element, ParserContext) 方法,解析 XML 元素。代码如下:

1
public BeanDefinition parse(Element element, ParserContext parserContext) {    return parse(element, parserContext, beanClass, required);}

友情提示:建议胖友,能够边看边调试。

3.2.1 创建 RootBeanDefinition

#parse(Element, ParserContext, beanClass, required) 的第 78 至 80 行,代码如下:

1
RootBeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClass(beanClass);beanDefinition.setLazyInit(false);
  • 默认 《Dubbo 用户指南 —— XML 配置》 lazyInit = false 。 FROM 引用缺省是延迟初始化的,只有引用被注入到其它 Bean,或被 getBean() 获取,才会初始化。如果需要饥饿加载,即没有人引用也立即生成动态代理,可以配置:

<dubbo:reference … init=“true” />

3.2.2 处理 Bean 的编号

#parse(Element, ParserContext, beanClass, required) 的第 81 至 111 行,代码如下:

1
// 解析配置对象的 id 。若不存在,则进行生成。String id = element.getAttribute("id");if ((id == null || id.length() == 0) && required) {    // 生成 id 。不同的配置对象,会存在不同。    String generatedBeanName = element.getAttribute("name");    if (generatedBeanName == null || generatedBeanName.length() == 0) {        if (ProtocolConfig.class.equals(beanClass)) {            generatedBeanName = "dubbo";        } else {            generatedBeanName = element.getAttribute("interface");        }    }    if (generatedBeanName == null || generatedBeanName.length() == 0) {        generatedBeanName = beanClass.getName();    }    id = generatedBeanName;    // 若 id 已存在,通过自增序列,解决重复。    int counter = 2;    while (parserContext.getRegistry().containsBeanDefinition(id)) {        id = generatedBeanName + (counter++);    }}if (id != null && id.length() > 0) {    if (parserContext.getRegistry().containsBeanDefinition(id)) {        throw new IllegalStateException("Duplicate spring bean id " + id);    }    // 添加到 Spring 的注册表    parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);    // 设置 Bean 的 `id` 属性值    beanDefinition.getPropertyValues().addPropertyValue("id", id);}
  • 第 2 至 22 行:解析 Bean 的 id 。若不存在,则自动进行生成。
    • 第 5 至 16 行:生成 id 。规则为 name > 特殊规则 > className 。
    • 第 17 至 21 行:若 id 在 Spring 注册表已经存在,通过添加自增序列 作为后缀,避免冲突。
  • 第 28 行:添加 Bean 到 Spring 注册表。
  • 第 30 行:设置 Bean 的 id 。

3.2.3 处理 特殊情况

#parse(Element, ParserContext, beanClass, required) 的第 113 至 128 行,代码如下:

1
// 处理 `<dubbo:protocol` /> 的特殊情况if (ProtocolConfig.class.equals(beanClass)) {    // 需要满足第 220 至 233 行。    // 例如:【顺序要这样】    // <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" protocol="dubbo" ref="demoService"/>    // <dubbo:protocol id="dubbo" name="dubbo" port="20880"/>    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));            }        }    }}

「3.2.7 循环 Bean 对象的 setting 方法,将属性赋值到 Bean 对象」 统一解析。

3.2.4 处理 的 class 属性

#parse(Element, ParserContext, beanClass, required) 的第 129 至 142 行,代码如下:

1
// 处理 `<dubbo:service />` 的属性 `class`} else if (ServiceBean.class.equals(beanClass)) {    // 处理 `class` 属性。例如  <dubbo:service id="sa" interface="com.alibaba.dubbo.demo.DemoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" >    String className = element.getAttribute("class");    if (className != null && className.length() > 0) {        // 创建 Service 的 RootBeanDefinition 对象。相当于内嵌了 <bean class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />        RootBeanDefinition classDefinition = new RootBeanDefinition();        classDefinition.setBeanClass(ReflectUtils.forName(className));        classDefinition.setLazyInit(false);        // 解析 Service Bean 对象的属性        parseProperties(element.getChildNodes(), classDefinition);        // 设置 `<dubbo:service ref="" />` 属性        beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));    }
  • 处理 大多数 场景下的处理, 情况下我们不这么使用,包括官方文档也没提供这种方式的说明。当配置 class 属时,会自动创建 Service Bean 对象,而无需再配置 ref 属性,指向 Service Bean 对象。示例如下:
1
2
3
4
<bean id="demoDAO" class="com.alibaba.dubbo.demo.provider.DemoDAO" />
<dubbo:service id="sa" interface="com.alibaba.dubbo.demo.DemoService"  class="com.alibaba.dubbo.demo.provider.DemoServiceImpl">
  <property name="demoDAO" ref="demoDAO" />
</dubbo:service>

```plain text

3.2.5 解析 的内嵌子元素

#parse(Element, ParserContext, beanClass, required) 的第 143 至 145 行,代码如下:

1
// 解析 `<dubbo:provider />` 的内嵌子元素 `<dubbo:service />`} else if (ProviderConfig.class.equals(beanClass)) {    parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);

3.2.6 解析 的内嵌子元素

#parse(Element, ParserContext, beanClass, required) 的第 146 至 149 行,代码如下:

1
// 解析 `<dubbo:consumer />` 的内嵌子元素 `<dubbo:reference />`} else if (ConsumerConfig.class.equals(beanClass)) {    parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);}

3.2.7 循环 Bean 对象的 setting 方法,将属性赋值到 Bean 对象

#parse(Element, ParserContext, beanClass, required) 的第 150 至 273 行,代码如下:

1
Set<String> props = new HashSet<String>(); // 已解析的属性集合ManagedMap parameters = null; //// 循环 Bean 对象的 setting 方法,将属性添加到 Bean 对象的属性赋值for (Method setter : beanClass.getMethods()) {    String name = setter.getName();    if (name.length() > 3 && name.startsWith("set")        && Modifier.isPublic(setter.getModifiers())        && setter.getParameterTypes().length == 1) { // setting && public && 唯一参数        Class<?> type = setter.getParameterTypes()[0];        // 添加 `props`        String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), "-");        props.add(property);        // getting && public && 属性值类型统一        Method getter = null;        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) {            }        }        if (getter == null            || !Modifier.isPublic(getter.getModifiers())            || !type.equals(getter.getReturnType())) {            continue;        }        // 解析 `<dubbo:parameters />`        if ("parameters".equals(property)) {            parameters = parseParameters(element.getChildNodes(), beanDefinition);            // 解析 `<dubbo:method />`        } else if ("methods".equals(property)) {            parseMethods(id, element.getChildNodes(), beanDefinition, parserContext);            // 解析 `<dubbo:argument />`        } else if ("arguments".equals(property)) {            parseArguments(id, element.getChildNodes(), beanDefinition, parserContext);        } else {            String value = element.getAttribute(property);            if (value != null) {                value = value.trim();                if (value.length() > 0) {                    // 不想注册到注册中心的情况,即 `registry=N/A` 。                    if ("registry".equals(property) && RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(value)) {                        RegistryConfig registryConfig = new RegistryConfig();                        registryConfig.setAddress(RegistryConfig.NO_AVAILABLE);                        beanDefinition.getPropertyValues().addPropertyValue(property, registryConfig);                        // 多注册中心的情况                    } else if ("registry".equals(property) && value.indexOf(',') != -1) {                        parseMultiRef("registries", value, beanDefinition, parserContext);                        // 多服务提供者的情况                    } else if ("provider".equals(property) && value.indexOf(',') != -1) {                        parseMultiRef("providers", value, beanDefinition, parserContext);                        // 多协议的情况                    } else if ("protocol".equals(property) && value.indexOf(',') != -1) {                        parseMultiRef("protocols", value, beanDefinition, parserContext);                    } else {                        Object reference;                        // 处理属性类型为基本属性的情况                        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;                        // 处理在 `<dubbo:provider />` 或者 `<dubbo:service />` 上定义了 `protocol` 属性的 兼容性。                        } else if ("protocol".equals(property)                                && ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(value) // 存在该注册协议的实现                                && (!parserContext.getRegistry().containsBeanDefinition(value) // Spring 注册表中不存在该 `<dubbo:provider />` 的定义                                    || !ProtocolConfig.class.getName().equals(parserContext.getRegistry().getBeanDefinition(value).getBeanClassName())) // Spring 注册表中存在该编号,但是类型不为 ProtocolConfig 。                                ) {                            // 目前,`<dubbo:provider protocol="" />` 推荐独立成 `<dubbo:protocol />`                            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;                        // 处理 `onreturn` 属性                        } else if ("onreturn".equals(property)) {                            // 按照 `.` 拆分                            int index = value.lastIndexOf(".");                            String returnRef = value.substring(0, index);                            String returnMethod = value.substring(index + 1);                            // 创建 RuntimeBeanReference ,指向回调的对象                            reference = new RuntimeBeanReference(returnRef);                            // 设置 `onreturnMethod` 到 BeanDefinition 的属性值                            beanDefinition.getPropertyValues().addPropertyValue("onreturnMethod", returnMethod);                            // 处理 `onthrow` 属性                        } else if ("onthrow".equals(property)) {                            // 按照 `.` 拆分                            int index = value.lastIndexOf(".");                            String throwRef = value.substring(0, index);                            String throwMethod = value.substring(index + 1);                            // 创建 RuntimeBeanReference ,指向回调的对象                            reference = new RuntimeBeanReference(throwRef);                            // 设置 `onthrowMethod` 到 BeanDefinition 的属性值                            beanDefinition.getPropertyValues().addPropertyValue("onthrowMethod", throwMethod);                        // 通用解析                        } else {                            // 指向的 Service 的 Bean 对象,必须是单例                            if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {                                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\" ...>");                                }                            }                            // 创建 RuntimeBeanReference ,指向 Service 的 Bean 对象                            reference = new RuntimeBeanReference(value);                        }                        // 设置 BeanDefinition 的属性值                        beanDefinition.getPropertyValues().addPropertyValue(property, reference);                    }                }            }        }    }}
  • 第 1 行:已解析的属性集合 props 。该属性在 「3.2.8 将 XML 元素未遍历到的属性,添加到 parameters 集合中」 会使用到。
  • 第 2 行:解析的参数集合 parameters 。
  • 第 4 行:循环 Bean 对象的 setting 方法,将属性添加到 Bean 对象的属性赋值。
  • 第 5 至 8 行:判断方法符合 setting && public && 唯一参数的条件。
  • 第 10 至 12 行:添加属性名 到 props 。
  • 第 13 至 27 行:判断方法符合 getting && public && 返回类型与 setting 参数类型一致 的条件。
  • 第 28 至 30 行:调用 #parseParameters(id, nodeList, beanDefinition, parserContext)「3.3.6 parseParameters」 方法,解析 标签。详细解析见 方法。
  • 第 31 至 33 行:调用 #parseMethods(id, nodeList, beanDefinition, parserContext)「3.3.4 parseMethods」 方法,解析 标签。详细解析见 方法。
  • 第 34 至 36 行:调用 #parseArguments(id, nodeList, beanDefinition, parserContext)「3.3.5 parseArguments」 方法,解析 标签。详细解析见 方法。
  • 第 42 至 46 行:处理 “registry” 属性,不想注册到注册中心的情况,即 registry=N/A 。
  • 第 47 至 55 行:调用 #parseMultiRef(property, beanDefinition, parserContext)「3.3. parseMultiRef」 方法,处理多注册中心、多服务提供者、多协议的情况下。详细解析见 方法。
  • 第 58 至 70 行:处理属性类型为基础属性#isPrimitive(Class<?>) ( )的情况。
  • 第 71 至 84 行:这块比较复杂《Dubbo 文档 —— schema 配置参考手册》 。“protocol” 属性,在多个标签中会使用,例如 或者 等等。根据 的说明,“protocol” 代表的是指向的 的编号( id )。
    • 【此处是猜测】但是,早期的版本,协议名 “protocol” 属性,代表的是 。这就麻烦了,和现有的逻辑有冲突啊!
    • 那怎么解决呢?优先以指向的 编号协议名 的 ( id ) 为准备,否则认为是 。
    • 那实际在解析 Bean 对象时,带有 之后 “protocol” 属性的标签,无法保证一定在 解析。那咋整呢?
      • 如果带有 先直接覆盖 “protocol” 属性的标签 解析,先【第 221 至 223 行】 创建 ProtocolConfig 对象并设置到 “protocol” 属性,再【第 119 至 127 行】在 解析后,进行 。这样,如果不存在 的情况,最多不进行覆盖呢。
      • 如果带有 “protocol” 属性的标签 解析,无需走上述流程,走【第 257 至 264 行】即可。
      • 如果这段无法理解,可以给我留言,有点绕。
  • 第 85 至 94 行 ||《Dubbo 用户指南 —— 事件通知》 第 244 至 253 行:处理 “onreturn” 和 “onthrow” 属性。该属性用于 功能。
  • 第 105 至 115 行:剩余情况,通用解析,创建 RuntimeBeanReference 对象。例如, 的 “ref” 属性。
  • 第 118 行:设置 Bean 的属性值。该属性值来自第 206 至 265 行代码的逻辑

3.2.8 将 XML 元素未遍历到的属性,添加到 parameters 集合中

#parse(Element, ParserContext, beanClass, required) 的第 274 至 290 行,代码如下:

```plain text 274: // 将 XML 元素,未在上面遍历到的属性,添加到 parameters 集合中。目前测试下来,不存在这样的情况。 275: NamedNodeMap attributes = element.getAttributes(); 276: int len = attributes.getLength(); 277: for (int i = 0; i < len; i++) { 278: Node node = attributes.item(i); 279: String name = node.getLocalName(); 280: if (!props.contains(name)) { 281: if (parameters == null) { 282: parameters = new ManagedMap(); 283: } 284: String value = node.getNodeValue(); 285: parameters.put(name, new TypedStringValue(value, String.class)); 286: } 287: } 288: if (parameters != null) { 289: beanDefinition.getPropertyValues().addPropertyValue(“parameters”, parameters); 290: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 第 275 至 287 行:将 XML 元素,**未在上面遍历到的属性**
,添加到
parameters
集合中。目前测试下来,不存在这样的情况。
- 第 288 至 290 行:设置 Bean 的
parameters
。

## 3.3 解析方法【辅流程】

### 3.3.1 parseProperties

[#parseProperties(NodeList, RootBeanDefinition)](https://github.com/YunaiV/dubbo/blob/55227e1897030c42342c65b2fce8e1705844a531/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java#L368-L399) 方法,解析 Service Bean 对象的属性们。代码如下:

```java
/** * 解析 <dubbo:service class="" /> 情况下,内涵的 `<property />` 的赋值。 * * @param nodeList 子元素数组 * @param beanDefinition Bean 定义对象 */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");                        // value                        if (value != null && value.length() > 0) {                            beanDefinition.getPropertyValues().addPropertyValue(name, value);                            // ref                        } 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=\"...\" />");                        }                    }                }            }        }    }}
  • 第 11 至 13 行: 解析 标签。
  • 第 18 至 20 行:优先使用 “value” 属性。
  • 第 21 至 23 行:其次使用 “ref” 属性。
  • 第 24 至 27 行:属性补全,抛出异常。

3.3.2 parseNested

#parseNested(…) 方法,解析内嵌的指向的子 XML 元素。代码如下:

```plain text 1: /** 2: * 解析内嵌的指向的子 XML 元素 3: * 4: * @param element 父 XML 元素 5: * @param parserContext Spring 解析上下文 6: * @param beanClass 内嵌解析子元素的 Bean 的类 7: * @param required 是否需要 Bean 的 id 属性 8: * @param tag 标签 9: * @param property 父 Bean 对象在子元素中的属性名 10: * @param ref 指向 11: * @param beanDefinition 父 Bean 定义对象 12: */ 13: private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required, String tag, 14: String property, String ref, BeanDefinition beanDefinition) { 15: NodeList nodeList = element.getChildNodes(); 16: if (nodeList != null && nodeList.getLength() > 0) { 17: boolean first = true; 18: for (int i = 0; i < nodeList.getLength(); i++) { 19: Node node = nodeList.item(i); 20: if (node instanceof Element) { 21: if (tag.equals(node.getNodeName()) 22: || tag.equals(node.getLocalName())) { // 这三行,判断是否为指定要解析的子元素 23: // 【TODO 8008】 芋艿,default 是干锤子的 24: if (first) { 25: first = false; 26: String isDefault = element.getAttribute(“default”); 27: if (isDefault == null || isDefault.length() == 0) { 28: beanDefinition.getPropertyValues().addPropertyValue(“default”, “false”); 29: } 30: } 31: // 解析子元素,创建 BeanDefinition 对象 32: BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required); 33: // 设置子 BeanDefinition ,指向父 BeanDefinition 。 34: if (subDefinition != null && ref != null && ref.length() > 0) { 35: subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref)); 36: } 37: } 38: } 39: } 40: } 41: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 第 20 至 22 行:**只指定标签内嵌**
解析
。目前有
的
和
标签。
- 第 32 行:解析指定标签,创建子 Bean 对象。
- 第 33 至 36 行:设置创建的**子父**
Bean 对象,指向
Bean 对象。

### 3.3.3 parseMultiRef

[#parseMultiRef(property, beanDefinition, parserContext)](https://github.com/YunaiV/dubbo/blob/55227e1897030c42342c65b2fce8e1705844a531/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java#L301-L324) 方法,解析多指向的情况,例如多注册中心,多协议等等。代码如下:

```java
/** * 解析多指向的情况,例如多注册中心,多协议等等。 * * @param property 属性 * @param value 值 * @param beanDefinition Bean 定义对象 * @param parserContext Spring 解析上下文 */@SuppressWarnings("unchecked")private static void parseMultiRef(String property, String value, RootBeanDefinition beanDefinition,                                  ParserContext parserContext) {    String[] values = value.split("\\s*[,]+\\s*");    ManagedList list = null;    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));        }    }    beanDefinition.getPropertyValues().addPropertyValue(property, list);}
  • 第 12 至 22 行:以 . 拆分 字符串,创建 RuntimeBeanReference 数组。
  • 第 23 行:设置 Bean 对象的指定属性值。

3.3.4 parseMethods

#parseMethods(id, nodeList, beanDefinition, parserContext) 方法,解析 标签。代码如下:

```plain text 1: /** 2: * 解析 <dubbo:method /> 3: * 4: * @param id Bean 的 id 属性。 5: * @param nodeList 子元素节点数组 6: * @param beanDefinition Bean 定义对象 7: * @param parserContext 解析上下文 8: */ 9: @SuppressWarnings(“unchecked”) 10: private static void parseMethods(String id, NodeList nodeList, RootBeanDefinition beanDefinition, 11: ParserContext parserContext) { 12: if (nodeList != null && nodeList.getLength() > 0) { 13: ManagedList methods = null; // 解析的方法数组 14: for (int i = 0; i < nodeList.getLength(); i++) { 15: Node node = nodeList.item(i); 16: if (node instanceof Element) { 17: Element element = (Element) node; 18: if (“method”.equals(node.getNodeName()) 19: || “method”.equals(node.getLocalName())) { // 这三行,判断值解析 <dubbo:method /> 20: // 方法名不能为空 21: String methodName = element.getAttribute(“name”); 22: if (methodName == null || methodName.length() == 0) { 23: throw new IllegalStateException(“ name attribute == null"); 24: } 25: if (methods == null) { 26: methods = new ManagedList(); 27: } 28: // 解析 ``,创建 BeanDefinition 对象 29: BeanDefinition methodBeanDefinition = parse(((Element) node), parserContext, MethodConfig.class, false); 30: // 添加到 `methods` 中 31: String name = id + "." + methodName; 32: BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(methodBeanDefinition, name); 33: methods.add(methodBeanDefinitionHolder); 34: } 35: } 36: } 37: if (methods != null) { 38: beanDefinition.getPropertyValues().addPropertyValue("methods", methods); 39: } 40: } 41: }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- 第 13 行:解析的方法数组
methods
。
- 第 16 至 19 行:**只**
解析
标签。
- 第 29 行:调用 [#parse(Element, ParserContext)](https://github.com/YunaiV/dubbo/blob/55227e1897030c42342c65b2fce8e1705844a531/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java#L516-L518)**主流程**
方法,
,解析
标签,创建子 Bean 对象。
- 第 33 至 36 行:设置创建的**子父**
Bean 对象,指向
Bean 对象。
- 第 30 至 33 行:添加子 Bean 对象到
methods
中。
- 第 37 至 39 行:设置 Bean 的
methods
。

### 3.3.5 parseArguments

[#parseArguments(id, nodeList, beanDefinition, parserContext)](https://github.com/YunaiV/dubbo/blob/55227e1897030c42342c65b2fce8e1705844a531/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java#L436-L476) 方法,解析  标签。

和 [「3.3.4 parseMethods」](http://svip.iocoder.cn/Dubbo/configuration-xml/#) 基本一致, 胖友点击方法,直接查看代码。

### 3.3.6 parseParameters

[#parseParameters(id, nodeList, beanDefinition, parserContext)](https://github.com/YunaiV/dubbo/blob/55227e1897030c42342c65b2fce8e1705844a531/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/schema/DubboBeanDefinitionParser.java#L401-L434) 方法,解析  标签。代码如下:

```java
/** * 解析 `<dubbo:parameter />` * * @param nodeList 子元素节点数组 * @param beanDefinition Bean 定义对象 * @return 参数集合 */@SuppressWarnings("unchecked")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) {                if ("parameter".equals(node.getNodeName())                    || "parameter".equals(node.getLocalName())) { // 这三行,只解析子元素中的 `<dubbo:parameter />`                    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")); // 【TODO 8007】 <dubbo:parameter hide=“” /> 的用途                    if (hide) {                        key = Constants.HIDE_KEY_PREFIX + key;                    }                    parameters.put(key, new TypedStringValue(value, String.class));                }            }        }        return parameters;    }    return null;}
  • 第 13 行:解析的参数集合 parameters 。
  • 第 20 至 27 行:添加 “key” “value” 到 parameters 。

666. 彩蛋识星球

稍显啰嗦的一篇文章,希望胖友能理解。

关于这块内容,在推荐下肥朝写的 《Dubbo源码解析 —— 简单原理、与 Spring 融合》

本文由作者按照 CC BY 4.0 进行授权