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

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

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);}
- 调用 #parse(Element, ParserContext, beanClass, required)真正 方法, 解析 XML 元素。该方法十分冗长,近 200+ 行。另外算上内部会调用其他方法,整体 500+ 行左右。 下面,我们来将方法切换理解。
友情提示:建议胖友,能够边看边调试。
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
- 通过这种方式,可以使用
标签,设置 Service Bean 的属性。 ```
- 第 6 至 9 行:创建 Service 的 Bean 对象。
- 第 11 行:调用 #parseProperties(NodeList, RootBeanDefinition)「3.3.1 parseProperties」 方法,解析 Service Bean 对象的属性们。详细解析见 方法。
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);
- 调用 #parseNested(…)「3.3.2 parseNested」 方法,解析内嵌的指向的子 XML 元素。详细解析见 方法。
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);}
- 调用 #parseNested(…) 方法,解析内嵌的指向的子 XML 元素。
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(“
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 融合》 。