3.3.2. BeanDefinition解析与注册之BeanDefinitionDocumentReader
把XML配置文件转为Document后,就进入了BeanDefinition的解析注册的核心流程。
int count = registerBeanDefinitions(doc, resource);
这里返回的count是本次注册的BeanDefinition的数量。
// 源码位置:org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// Registry中已存在的BeanDefinition数量
int countBefore = getRegistry().getBeanDefinitionCount();
// BeanDefinition解析注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 当前Registry中BeanDefinition总数量-本次未注册前的数量=返回本次BeanDefinition注册数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
XML转为Document对象之后,XmlBeanDefinitionReader将BeanDefinition解析注册的工作交给了BeanDefinitionDocumentReader的子类DefaultBeanDefinitionDocumentReader来处理documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
。
3.3.2.1. createReaderContext(resource)创建XmlReaderContext对象
// 源码位置:org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createReaderContext
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
XmlReaderContext继承至ReaderContext,ReaderContext是BeanDefinition读取过程中传递和封装所有相关配置和状态的上下文。包括传递Resource资源、错误及警告信息、触发注册过程中的事件等。
XmlReaderContext扩展了ReaderContext,与XmlBeanDefinitionReader一起使用,提供对在XmlBeanDefinitionReader中配置的NamespaceHandlerResolver的访问,并且作为上下文扩展了XmlBeanDefinitionReader对象的传递及XmlBeanDefinitionReader对象中相关属性和配置的获取。
// 源码位置:org.springframework.beans.factory.xml.XmlReaderContext#XmlReaderContext
public XmlReaderContext(
Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor,
XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
super(resource, problemReporter, eventListener, sourceExtractor);
// 扩展项
this.reader = reader;
this.namespaceHandlerResolver = namespaceHandlerResolver;
}
创建XmlReaderContext的参数new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver());
。
- resource:XML的Resource对象;
- problemReporter:异常信息(fatal、error、warning)记录,具体由
new FailFastProblemReporter()
实现; - eventListener:BeanDefinition读取过程中接收component(组件)、alias(别名)、导入(import)注册(registrations)动作事件的回调,具体由
new EmptyReaderEventListener()
实现,我们会看到该类的方法实现是空的,在事件回调的时候并不做任何操作; - sourceExtractor:source元数据的提取类,用于将source元数据关联到BeanDefinition元数据,具体由
new NullSourceExtractor()
实现,它返回了一个null,因此我们不做过多的解释; - this:当前XmlBeanDefinitionReader对象;
- getNamespaceHandlerResolver():我们在创建ReaderContext的时候说过,XmlReaderContext扩展了ReaderContext,与XmlBeanDefinitionReader一起使用,提供对在XmlBeanDefinitionReader中配置的NamespaceHandlerResolver的访问,那么这个NamespaceHandlerResolver就是在这里获取的。
3.3.2.1. 获取NamespaceHandlerResolver
// 源码位置:org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getNamespaceHandlerResolver
/**
* Lazily create a default NamespaceHandlerResolver, if not set before.
* @see #createDefaultNamespaceHandlerResolver()
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
createDefaultNamespaceHandlerResolver()
// 源码位置:org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver
/**
* Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
* <p>The default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
* @see DefaultNamespaceHandlerResolver#DefaultNamespaceHandlerResolver(ClassLoader)
*/
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
在此之前我们在回顾一下什么是命名空间:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
比如:xmlns="http://www.springframework.org/schema/beans"
、xmlns:p="http://www.springframework.org/schema/p"
,还有我们常见的xmlns:c="http://www.springframework.org/schema/c"
、xmlns:p="http://www.springframework.org/schema/p"
等。这其中有默认命名空间xmlns=,也有提供扩展功能的命名独立命名空间xmlns:p=等等。
命名空间处理程序(NamespaceHandler)是一个接口,不同命名空间的标签配置会对应到不同的NamespaceHandler实现类,以完成XML的解析工作。那么怎么根据命名空间找到对应的NamespaceHandler实现类呢?这就用到了我们要说的NamespaceHandlerResolver,这是一个接口,具体的实现由new DefaultNamespaceHandlerResolver(cl)
完成。我们看一下它是如何实现的命名空间和各NamespaceHandler实现类之间的对应关系的,打开DefaultNamespaceHandlerResolver类,找到getHandlerMappings()方法。
// 源码位置:org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings
/**
* The location to look for the mapping files. Can be present in multiple JAR files.
*/
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
这一行PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
中的this.handlerMappingsLocation默认就是全局变量DEFAULT_HANDLER_MAPPINGS_LOCATION的值META-INF/spring.handlers。我们打开这个文件看到使用键值对的方式配置了其对应关系:
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
至此,我们已经了解
createReaderContext(resource)
的工作原理并得到了一个XmlReaderContext对象。现在我们可以放心的将后续工作交个BeanDefinitionDocumentReader来处理了:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
。