前面讲到利用XML配置文件示范Dubbo的使用,比如服务提供者的provider.xml配置文件内容为:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://dubbo.apache.org/schema/dubbo
http:dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="test-xml-provider" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://10.19.50.225:2181"/>
<dubbo:protocol name="dubbo" port="20890" />
<dubbo:service interface="com.starry.testxmlprovider.service.TestDubbo" ref="dubboService" protocol="dubbo" />
<bean id="dubboService" class="com.starry.testxmlprovider.service.impl.TestDubboImpl"/>
</beans>
可以看到Dubbo自定义标签:dubbo:application,dubbo:registry,dubbo:protocol等等,前面我们已经分析过Dubbo标签的定义,并且说明自定义标签的使用和解析过程;本篇博文将针对dubbo自定义标签的解析作一个深入的剖析。
定义好provider.xml配置文件后,通过ClassPathXmlApplicationContext加载xml配置文件。下面给出调用链:
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String)
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
以上调用链都是ClassPathXmlApplicationContext的处理过程,为了控制篇幅这里就不涉及,下面则开始涉及到dubbo框架的内容。
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 是否为默认命名空间,默认为http://www.springframework.org/schema/beans
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
//获取element结点,即xml的标签
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
// 针对非默认命名空间,则自定义解析元素
delegate.parseCustomElement(root);
}
}
除了命名空间为bean的标签外,基本都是自定义,类似于AOP,Dubbo等。自定义标签的解析则委托给BeanDefinitionParserDelegate类解析。
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
return this.parseCustomElement(ele, (BeanDefinition)null);
}
parseCustomElement(root,null)方法主要完成三件事:
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 根据xml标签获取url,如根据dubbo获取xmlns的值
String namespaceUri = this.getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
} else {
// 根据xmlns获取命名空间handler,如dubbo-DubboNamespaceHandler
// 获取handler的过程就会执行上面提到的init()方法
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 如果没有定义namespaceHandler则抛出异常
if (handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
} else {
// 调用自定义的namespaceHandler处理xml结点
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
}
调用链路为:
NamespaceHandlerSupport.parse(Element,ParserContext)
注意解析XML结点后返回的是BeanDefinition对象(实现类RootBeanDefinition的对象).
findParserForElement(Element,ParserContext)
根据该xml结点的localName查找BeanDefinitionParser对象;注意parsers对象,没错,它就是DubboNamespaceHandler.init()方法执行时放置name和parsers之间映射关系(map)的对象。所以这里返回的BeanDefinitionParser对象是DubboBeanDefinitionParser类的对象。
DubboBeanDefinitionParser.parse(Element,ParserContext)
最终调用parse(Element,ParserContext,Class,Boolean),该方法是核心方法所以比较长,下面省略部分并给出关键代码:
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
// 声明返回beanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanClass);
beanDefinition.setLazyInit(false);
// 获取bean的id
String id = element.getAttribute("id");
String className;
int var7;
if (StringUtils.isEmpty(id) && required) {
// 如果没有显示的配置节点的id,那么分为下面几种情况
// 1.如果节点配置name属性,则赋值给id;
// 2.如果节点没有配置name属性,则分下面两种情况:
// 2.1 如果此节点是“protocol”,则id赋值为“dubbo”;
// 2.2 否则获取节点中interface属性的值,若其值为空则获取节点对应的bean的name值赋值给id,若有值则直接赋值给id
}
if (id != null && id.length() > 0) {
// 若拥有id重复的RootBeanDefinition,则抛出异常
if (parserContext.getRegistry().containsBeanDefinition(id)) {
throw new IllegalStateException("Duplicate spring bean id " + id);
}
// 反之id为key,rootBeanDefinition为value建立映射关系
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
// 把id值写入rootBeanDefinition的属性中
beanDefinition.getPropertyValues().addPropertyValue("id", id);
}
// 若该结点的元素为“protocol”,则…
if (ProtocolConfig.class.equals(beanClass)) {
// 为xml文件中含有protocol属性的节点生成一个RuntimeBeanReference对象
String[] var24 = parserContext.getRegistry().getBeanDefinitionNames();
var7 = var24.length;
for(int var8 = 0; var8 < var7; ++var8) {
String name = var24[var8];
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));
}
}
}
//若该结点的元素为“service”,则…
} else if (ServiceBean.class.equals(beanClass)) {
// 获取属性为“class”的值,并为此生成一个新的RootBeanDefinition对象
// 该新对象以ref为名放入外围rootBeanDefinition对象中
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"));
}
//若该结点的元素为“provider”,则…
} else if (ProviderConfig.class.equals(beanClass)) {
// 针对该结点“service”属性,迭代生成新的subBeanDefinition
// subBeanDefinition的provider值为RuntimeBeanReference(id),建立关系
parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition);
//若该结点的元素为“consumer”,则…
} else if (ConsumerConfig.class.equals(beanClass)) {
// 针对该结点“reference”属性,迭代生成新的subBeanDefinition
// subBeanDefinition的consumer值为RuntimeBeanReference(id),建立关系
parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition);
}
Set<String> props = new HashSet();
ManagedMap parameters = null;
// 通过反射获取bean的所有方法, 然后循环遍历方法获取setXXX()格式的方法,获取property
// 然后根据property执行不同的逻辑,如property为parameters时执行parseParameters()
// 针对parameters,methods,arguments等分别执行不同的逻辑
// 在这里要吐槽下编码者,不写注释要调试很多次才能看懂
Method[] var28 = beanClass.getMethods();
int len = var28.length;
// …
// 针对element结点在实体bean中没有setXXX()方法的属性,统一放在parameters中
NamedNodeMap attributes = element.getAttributes();
len = attributes.getLength();
for(i = 0; i < len; ++i) {
Node node = attributes.item(i);
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);
}
return beanDefinition;
}
方法解析xml结点并以此生成bean对象。