Spring XML默认标签配置解析注册Bean源码分析(四)——BeanDefinitionDocumentReader

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

  1. resource:XML的Resource对象;
  2. problemReporter:异常信息(fatal、error、warning)记录,具体由new FailFastProblemReporter()实现;
  3. eventListener:BeanDefinition读取过程中接收component(组件)、alias(别名)、导入(import)注册(registrations)动作事件的回调,具体由new EmptyReaderEventListener()实现,我们会看到该类的方法实现是空的,在事件回调的时候并不做任何操作;
  4. sourceExtractor:source元数据的提取类,用于将source元数据关联到BeanDefinition元数据,具体由new NullSourceExtractor()实现,它返回了一个null,因此我们不做过多的解释;
  5. this:当前XmlBeanDefinitionReader对象;
  6. 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));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值