1,配置信息

先看看dubbo官方文档给出的初始化过程:

基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler。所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。

找到dubbo.jar中的spring.handlers及DubboBeanDefinitionParser:

spring.handlers:

http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandlerhttp\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

DubboBeanDefinitionParser:

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()); }}

上述registerBeanDefinitionParser注册对应标签的解释器,即当遇到"application"(即<dubbo:application/>)时,使用
new DubboBeanDefinitionParser(ApplicationConfig.class, true)对其进行解析。

dubbo配置文件及其意义:

1、xmlns="http://www.springframework.org/schema/beans" 声明xml文件默认的命名空间,表示未使用其他命名空间的所有标签的默认命名空间。 2、xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 声明XML Schema 实例,声明后就可以使用 schemaLocation 属性 3、xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"声明前缀为dubbo的命名空间,其惟一的作用是赋予命名空间一个惟一的名称。当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。即当spring解析以dubbo开头的标签时,会把其命名空间设置为"http://code.alibabatech.com/schema/dubbo",而这个命名空间名称和spring.handlers中的一个key是相同的,这即是它们产生联系的地方。4、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 http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" 这个从命名可以看出个大概,指定Schema的位置这个属性必须结合命名空间使用。这个属性有两个值,第一个值表示需要使用的命名空间。第二个值表示供命名空间使用的 XML schema 的位置。

2,源码解析

配置文件解析发生在容器refresh的obtainFreshBeanFactory阶段

调用栈:

进到XmlBeanDefinitionReader#doLoadBeanDefinitions中:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //将配置文件解析为Document对象,其中级联包含子节点,每个子节点代表一个配置项 Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } .......... }

继续往下走,来到DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,这里开始遍历解析子节点:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); 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)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); }}

由于是自定义的配置,将会进入delegate.parseCustomElement(ele);

public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //获取命名空间,如果是dubbo的配置,将会是"http://code.alibabatech.com/schema/dubbo" String namespaceUri = getNamespaceURI(ele); //更具命名空间,查找对应的Handler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

进到DefaultNamespaceHandlerResolver#resolve:

public NamespaceHandler resolve(String namespaceUri) { //从jar包的META-INF/spring.handlers中读取Handler信息 Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } //实例化Handler NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); //执行初始化方法,指定不同的标签各自使用的解析器 namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } ....... }}

进到getHandlerMappings方法:

private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized (this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isDebugEnabled()) { logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } } } } return this.handlerMappings;}

读取文件的具体操作就不跟了,回到DefaultNamespaceHandlerResolver#resolve,看看获取到的mapping:

其中一个则是以http://code.alibabatech.com/schema/dubbo 为键, DubboNamespaceHandler为值,因此,当解析dubbo的元素时,则会使用DubboNamespaceHandler

回到delegate.parseCustomElement,并进入到handler.parse中:

@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext);}

findParserForElement(element, parserContext),查找对应的解析器,即handler.init()中初始化的,然后对应解析器开始解析Element,最终在DubboBeanDefinitionParser#parse中进行构建RootBeanDefinition,并将构建好的RootBeanDefinition加入到容器中,并在容器refresh过程的finishBeanFactoryInitialization(beanFactory)中创建对应实例。