把文档转换为Document后,接下来的提取及注册Bean就是我们的重头戏,当程序拥有XMl文档文件的Document实例对象时,就会执行下面流程BD
首先我们来看5个主要的类
1、XmlBeanDefinitionReader提供接口注册方法,开始注册
2、DefaulteBeanDefinitionDocumentReader提供预解析以及解析之前和解析之后的扩展、同时获取委托者,根据条件调用委托者的相应方法如bean、beans、alias、import调用方法的委托
3、BeanDefinitionDelegate解析真正执行者
4、BeanDefinitionReaderUtils工具类执行创建注册BeanDefinition
5、XmlReaderContext存储XmlBeanDefinitionReader,通知存储监听、获取监听
解析上图内容XmlBDReader 的注册方法内部调用了DefaultDBDocumentReader的注册DefaultDBDocumentReader对象取出根节点、增强、解析、将解析结果从XmlReaderContext中取BdRegister使用BeanDefinitionReaderUtils的utils注册、2通知监听器
解释1 是 XmlBDReader registerBeanDefinitions创建2对象,调用2的注册BD
public int registerBeanDefinitions(Document doc, Resource resource)throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader来解析Document对象,完成BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获得容器中已经注册的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,BeanDefinitionDocumentReader只是个接口,具体的实现过程在DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 统计新的的BeanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
解释2 DefaultDBDocumentReader对象所做的事情就是取出根节点,对环境变量profile进行处理,并根据不同的标签进行委托解析,如果是默认标签使用spring提供的方式进行处理,识别标签类型,分别处理,<bean>标签处理交给BDParserDelegate,如果是自定义标签,直接交给DBParserDelegate
解释3 DBParserDelegate对象是解析的具体执行者,整体来说包括两个种解析方式默认解析和自定义解析,我们先看一下自定义解析。
自定义解析
public BeanDefinitionparseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取命名空间URI(就是获取beans标签的xmlns:aop或者xmlns:context属性的值)
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri ==null) {
return null;
}
// 根据不同的命名空间URI,去匹配不同的NamespaceHandler(一个命名空间对应一个NamespaceHandler)
// 此处会调用DefaultNamespaceHandlerResolver类的resolve方法
NamespaceHandler handler =this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler ==null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri +"]", ele);
return null;
}
// 调用匹配到的NamespaceHandler的解析方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
默认解析
首先获取bean的id属性和name属性,name属性解析得到别名。id和name检查容器中是否包含如果包含且不允许替换则抛出异常处理完成后开始默认bean标签解析
public BeanDefinitionparseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取命名空间URI(就是获取beans标签的xmlns:aop或者xmlns:context属性的值)
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri ==null) {
return null;
}
// 根据不同的命名空间URI,去匹配不同的NamespaceHandler(一个命名空间对应一个NamespaceHandler)
// 此处会调用DefaultNamespaceHandlerResolver类的resolve方法
NamespaceHandler handler =this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler ==null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri +"]", ele);
return null;
}
// 调用匹配到的NamespaceHandler的解析方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
好了到这里BD就解析完成了,接下来看注册和通知代码,注册默认标签的注册都在DefaultBeanDefinitionDocumentReader中
1、Bean的注册通知代码在processBeanDefinition
2、alias的注册通知代码在processAliasRegistration
3、import的加载和通知方法在importBeanDefinitionResource中
4、如果是自定义标签,读取spring所有工程的META-INF/spring.handlers文件,获取namespaceUri和NamespaceHandler的映射关系
这是Bean的注册通知代码 在DefaultBeanDefinitionDocumentReader的processBeanDefinition
try {
// Register the final decorated instance.
// 注册最终的BeanDefinition到BeanDefinitionRegistry(DefaultListableBeanFactory)
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() +"'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
这是alias的注册通知代码在DefaultBeanDefinitionDocumentReader的processAliasRegistration
try {
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name +"'", ele, ex);
}
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
这是import的加载和通知方法DefaultBeanDefinitionDocumentReader的importBeanDefinitionResource中
int importCount;
Resource relativeResource = getReaderContext().getResource().createRelative(location);
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount +" bean definitions from relative location [" + location +"]");
}
Resource[] actResArray = actualResources.toArray(new Resource[0]);
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
4、自定义标签解析
4.1首先获取命名空间url根据url查找对应的NameSpaceHandler
public BeanDefinitionparseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取命名空间URI(就是获取beans标签的xmlns:aop或者xmlns:context属性的值)
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri ==null) {
return null;
}
// 根据不同的命名空间URI,去匹配不同的NamespaceHandler(一个命名空间对应一个NamespaceHandler)
// 此处会调用DefaultNamespaceHandlerResolver类的resolve方法
NamespaceHandler handler =this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler ==null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri +"]", ele);
return null;
}
// 调用匹配到的NamespaceHandler的解析方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
4.2在项目resource目录中配置url和NameSpaceHandler的映射关系,初始化nameSpaceResolver初始化
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler