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:protocol
。2021-12-08 14:20:21
处是处理\<dubbo:service\>
,详细参考3.2.2:dubbo:service
。2021-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:provider
。2021-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);
}
}