1, the configuration information
Take a look at the initialization process dubbo official documents given by:
基于 dubbo.jar 内的 META-INF/spring.handlers 配置,Spring 在遇到 dubbo 名称空间时,会回调 DubboNamespaceHandler。
所有 dubbo 的标签,都统一用 DubboBeanDefinitionParser 进行解析,基于一对一属性映射,将 XML 标签解析为 Bean 对象。
Dubbo.jar found in spring.handlers and DubboBeanDefinitionParser:
spring.handlers:
http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
http\://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());
}
}
Above registerBeanDefinitionParser registered corresponding tag interpreter, i.e., when faced with "application" (i.e. <dubbo: application />), the use of
new DubboBeanDefinitionParser (ApplicationConfig.class, true) parse.
dubbo profiles and their meanings:
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, parsing source code
Configuration file parsing occur in the container refresh stage of obtainFreshBeanFactory
Call stack:
进到XmlBeanDefinitionReader#doLoadBeanDefinitions中:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将配置文件解析为Document对象,其中级联包含子节点,每个子节点代表一个配置项
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
..........
}
Continue to go down, come DefaultBeanDefinitionDocumentReader # parseBeanDefinitions, where traversing resolve child nodes:
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);
}
}
Since a custom configuration, will enter 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 method proceeds to:
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;
}
Read the file with a specific operation will not return to DefaultNamespaceHandlerResolver # resolve, to get a look at the mapping:
Wherein a bond is based http://code.alibabatech.com/schema/dubbo, DubboNamespaceHandler value, and therefore, when resolving dubbo elements, it will be used DubboNamespaceHandler
Back delegate.parseCustomElement, and into handler.parse in:
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return findParserForElement(element, parserContext).parse(element, parserContext);
}
findParserForElement (element, parserContext), to find the corresponding parser, i.e. Handler.init () initialized, and the corresponding parser starts parsing Element, eventually constructed in RootBeanDefinition DubboBeanDefinitionParser # parse, and the vessel was added to the constructed RootBeanDefinition and create corresponding instances in finishBeanFactoryInitialization (beanFactory) refresh process vessel.