文章

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 定义如下:

1
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
  • xmlns 为 http://code.alibabatech.com/schema/dubbo
  • xsd 为 META-INF/dubbo.xsd

2.2 dubbo.xsd

dubbo.xsd 定义如下:

dubbo.xsd

  • 元素的名称,定义了 <dubbo:application /> 等。例如,<dubbo:application /> 对应 ApplicationConfig
  • 内建数据类型的名称,定义了属性名。例如,name 对应 ApplicationConfig#name 属性。
  • 复杂类型,定义了。例如 applicationType 如下:

applicationType

2.3 spring.handlers

spring.handlers 定义如下:

1
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() 方法,定义了每个 <dubbo:xxx /> 对应的 org.springframework.beans.factory.xml.BeanDefinitionParser ,代码如下:

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

3. 解析

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

3.1 构造方法

构造方法 ,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * 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) 不存在时,自动生成编号。无需被其他应用引用的配置对象,无需自动生成编号。例如有 ReferenceBean

3.2 解析方法【主流程】

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

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

3.2.2 处理 Bean 的编号

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

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
// 解析配置对象的 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 处理 <dubbo:protocol /> 特殊情况

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 处理 `<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 处理 <dubbo:service /> 的 class 属性

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 处理 `<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>

3.2.5 解析 <dubbo:provider /> 的内嵌子元素

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

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

3.2.6 解析 <dubbo:consumer /> 的内嵌子元素

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

1
2
3
4
// 解析 `<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
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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) 方法,解析 <dubbo:parameter /> 标签。详细解析见 「3.3.6 parseParameters」 方法。
  • 第 31 至 33 行:调用 #parseMethods(id, nodeList, beanDefinition, parserContext) 方法,解析 <dubbo:method /> 标签。详细解析见 「3.3.4 parseMethods」 方法。
  • 第 34 至 36 行:调用 #parseArguments(id, nodeList, beanDefinition, parserContext) 方法,解析 <dubbo:argument /> 标签。详细解析见 「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 行:这块比较复杂。”protocol” 属性,在多个标签中会使用,例如 <dubbo:service /> 或者 <dubbo:provider /> 等等。根据 《Dubbo 文档 —— schema 配置参考手册》 的说明,”protocol” 代表的是指向的 <dubbo:protocol /> 的编号( id )。
    • 【此处是猜测】但是,早期的版本,”protocol” 属性,代表的是协议名。这就麻烦了,和现有的逻辑有冲突啊!
    • 那怎么解决呢?优先以指向的 编号( id ) 为准备,否则认为是协议名
    • 那实际在解析 Bean 对象时,带有 “protocol” 属性的标签,无法保证一定在之后解析。那咋整呢?
      • 如果带有 “protocol” 属性的标签解析,先【第 221 至 223 行】先直接覆盖创建 ProtocolConfig 对象并设置到 “protocol” 属性,再【第 119 至 127 行】在解析后,进行覆盖。这样,如果不存在冲突的情况,最多不进行覆盖呢。
      • 如果带有 “protocol” 属性的标签解析,无需走上述流程,走【第 257 至 264 行】即可。
      • 如果这段无法理解,可以给我留言,有点绕。
  • 第 85 至 94 行 ** ** 第 244 至 253 行:处理 “onreturn” 和 “onthrow” 属性。该属性用于 《Dubbo 用户指南 —— 事件通知》 功能。
  • 第 105 至 115 行:剩余情况,通用解析,创建 RuntimeBeanReference 对象。例如,<dubbo:service /> 的 “ref” 属性。
  • 第 118 行:设置 Bean 的属性值。该属性值来自第 206 至 265 行代码的逻辑

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

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

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

3.3 解析方法【辅流程】

3.3.1 parseProperties

#parseProperties(NodeList, RootBeanDefinition) 方法,解析 Service Bean 对象的属性们。代码如下:

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
/**
 * 解析 <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 行:解析 <property /> 标签。
  • 第 18 至 20 行:优先使用 "value" 属性。
  • 第 21 至 23 行:其次使用 "ref" 属性。
  • 第 24 至 27 行:属性补全,抛出异常。

3.3.2 parseNested

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

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
/**
 * 解析内嵌的指向的子 XML 元素
 *
 * @param element 父 XML 元素
 * @param parserContext Spring 解析上下文
 * @param beanClass 内嵌解析子元素的 Bean 的类
 * @param required 是否需要 Bean 的 `id` 属性
 * @param tag 标签
 * @param property 父 Bean 对象在子元素中的属性名
 * @param ref 指向
 * @param beanDefinition 父 Bean 定义对象
 */
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);
            if (node instanceof Element) {
                if (tag.equals(node.getNodeName())
                        || tag.equals(node.getLocalName())) { // 这三行,判断是否为指定要解析的子元素
                    // 【TODO 8008】 芋艿,default 是干锤子的
                    if (first) {
                        first = false;
                        String isDefault = element.getAttribute("default");
                        if (isDefault == null || isDefault.length() == 0) {
                            beanDefinition.getPropertyValues().addPropertyValue("default", "false");
                        }
                    }
                    // 解析子元素,创建 BeanDefinition 对象
                    BeanDefinition subDefinition = parse((Element) node, parserContext, beanClass, required);
                    // 设置子 BeanDefinition ,指向父 BeanDefinition 。
                    if (subDefinition != null && ref != null && ref.length() > 0) {
                        subDefinition.getPropertyValues().addPropertyValue(property, new RuntimeBeanReference(ref));
                    }
                }
            }
        }
    }
}
  • 第 20 至 22 行:只指定标签内嵌解析。目前有 <dubbo:provider /><dubbo:service /><dubbo:consumer /><dubbo:reference /> 标签。
  • 第 32 行:解析指定标签,创建子 Bean 对象。
  • 第 33 至 36 行:设置创建的 Bean 对象,指向 Bean 对象。

3.3.3 parseMultiRef

#parseMultiRef(property, beanDefinition, parserContext) 方法,解析多指向的情况,例如多注册中心,多协议等等。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * 解析多指向的情况,例如多注册中心,多协议等等。
 *
 * @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) 方法,解析 <dubbo:method /> 标签。代码如下:

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
/**
 * 解析 `<dubbo:method />`
 *
 * @param id Bean 的 `id` 属性。
 * @param nodeList 子元素节点数组
 * @param beanDefinition Bean 定义对象
 * @param parserContext 解析上下文
 */
@SuppressWarnings("unchecked")
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())) { // 这三行,判断值解析 `<dubbo:method />`
                    // 方法名不能为空
                    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 />`,创建 BeanDefinition 对象
                    BeanDefinition methodBeanDefinition = parse(((Element) node), parserContext, MethodConfig.class, false);
                    // 添加到 `methods` 中
                    String name = id + "." + methodName;
                    BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(methodBeanDefinition, name);
                    methods.add(methodBeanDefinitionHolder);
                }
            }
        }
        if (methods != null) {
            beanDefinition.getPropertyValues().addPropertyValue("methods", methods);
        }
    }
}
  • 第 13 行:解析的方法数组 methods
  • 第 16 至 19 行:解析 <dubbo:method /> 标签。
  • 第 29 行:调用 #parse(Element, ParserContext) 主流程方法,解析 <dubbo:method /> 标签,创建子 Bean 对象。
  • 第 30 至 33 行:添加子 Bean 对象到 methods 中。
  • 第 37 至 39 行:设置 Bean 的 methods

3.3.5 parseArguments

#parseArguments(id, nodeList, beanDefinition, parserContext) 方法,解析 <dubbo:argument /> 标签。

「3.3.4 parseMethods」 基本一致, 胖友点击方法,直接查看代码。

3.3.6 parseParameters

#parseParameters(id, nodeList, beanDefinition, parserContext) 方法,解析 <dubbo:parameter /> 标签。代码如下:

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
/**
 * 解析 `<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 进行授权