spring配置文件加载为BeanDefinition过程分析

1:写在前面

在实际工作中,我可能经常写如下的代码:

@Test
public void testBeanDefinitionLoad() {
    
    
    // 定义资源
    ClassPathResource classPathResource = new ClassPathResource("testbeandefinition.xml");
    // 定义IOC容器
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    // 定义bean定义读取器
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    // 通过bean定义读取器从资源中读取bean定义
    int i = xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
    System.out.println("bean定义的个数是:" + i);
}

其中使用的配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBeanDefinitionBean"
          class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>

    <bean id="testBeanDefinitionBean1"
          class="yudaosourcecode.spring.TestBeanDefinitionBean"></bean>
    <!-- 这里引入自己的话会发生org.springframework.beans.factory.BeanDefinitionStoreException异常 -->
    <!--<import resource="testbeandefinition.xml"/>-->
</beans>

这段代码是加载配置文件中的bean信息为spring中的BeanDefinition,这是在spring中存储我们定义的bean信息的数据结构,最终生成spring bean也依赖于此数据结构,代码最终运行结果如下:

bean定义的个数是:2

返回的就是我们在配置文件中定义的bean的个数。

2:loadBeanDefinitions

该方法完成从封装有配置文件的资源中加载beandefinition的功能,具体位置如下:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    
    
	// 其中EncodedResource是加了编码的资源,不影响主流程,可以无视
	return loadBeanDefinitions(new EncodedResource(resource));
}

继续:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    
    
	// 断言判断传入的资源不为null
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isTraceEnabled()) {
    
    
		logger.trace("Loading XML bean definitions from " + encodedResource);
	}
	// <2021-02-24 12:21>
	// 从resourcesCurrentlyBeingLoaded中获取当前线程已经或者是正在加载的资源集合
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	// 如果是不存在已经加载的资源则创建,注意这里是
	// 使用的HashSet,因为资源不允许重复
	if (currentResources == null) {
    
    
		currentResources = new HashSet<>(4);
		// 存储新创建的资源到resourcesCurrentlyBeingLoaded中
		this.resourcesCurrentlyBeingLoaded.set(currentResources);
	}
	// <2021-02-24 12:23>
	// 这里如果是资源在set集合中已经存在会返回false,此时代表资源重复
	// 直接抛出BeanDefinitionStoreException信息
	if (!currentResources.add(encodedResource)) {
    
    
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	try {
    
    
		// 获取对应的资源,然后从资源中获取输入流
		InputStream inputStream = encodedResource.getResource().getInputStream();
		try {
    
    
			// 封装到InputSource中
			InputSource inputSource = new InputSource(inputStream);
			// 如果有编码则使用,不影响主流程,可以忽略
			if (encodedResource.getEncoding() != null) {
    
    
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			// <2021-02-24 12:27>
			// 实现具体加载为BeanDefinition的过程
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		finally {
    
    
			inputStream.close();
		}
	}
	catch (IOException ex) {
    
    
		throw new BeanDefinitionStoreException(
				"IOException parsing XML document from " + encodedResource.getResource(), ex);
	}
	finally {
    
    
		// 删除资源
		currentResources.remove(encodedResource);
		// 如果删除当前资源后,没有其他资源,则清空threadlocal
		if (currentResources.isEmpty()) {
    
    
			this.resourcesCurrentlyBeingLoaded.remove();
		}
	}
}

<2021-02-24 12:21>处变量定义为private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>("XML bean definition resources currently being loaded");其中的类NamedThreadLocaljava.lang.ThreadLocal的子类,只是增加了名称的属性,源码如下:

public class NamedThreadLocal<T> extends ThreadLocal<T> {
    
    

	private final String name;

	public NamedThreadLocal(String name) {
    
    
		Assert.hasText(name, "Name must not be empty");
		this.name = name;
	}

	@Override
	public String toString() {
    
    
		return this.name;
	}
}

<2021-02-24 12:23>处是判断资源是否存在,主要是处理自己引入自己的情况,这样子会发生资源引入的死循环,所以处理一下,如下情况就会死循环:
在这里插入图片描述
如下代码测试并运行:

@Test
public void testBeanDefinitionStoreException() {
    
    
    // 定义资源
    ClassPathResource classPathResource = new ClassPathResource("testbeandefinition.xml");
    // 定义IOC容器
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    // 定义bean定义读取器
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    // 通过bean定义读取器从资源中读取bean定义
    xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
}

运行异常:
在这里插入图片描述
<2021-02-24 12:27>处源码如下:

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
    
    
	try {
    
    
		// <2021-02-24 16:01> 获取xml文件对应的文档对象
		Document doc = doLoadDocument(inputSource, resource);
		// <2021-02-24 16:02> 根据文档对象注册Bean信息
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
    
    
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	catch (BeanDefinitionStoreException ex) {
    
    
		throw ex;
	}
	catch (SAXParseException ex) {
    
    
		...snip...
	}
	catch (SAXException ex) {
    
    
		...snip...
	}
	catch (ParserConfigurationException ex) {
    
    
		...snip...
	}
	catch (IOException ex) {
    
    
		...snip...
	}
	catch (Throwable ex) {
    
    
		...snip...
	}
}

<2021-02-24 16:01>处源码如下:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    
    
	return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
			getValidationModeForResource(resource), isNamespaceAware());
}

代码getValidationModeForResource(resource)是获取验证模型,详细可以参考这里。代码this.documentLoader.loadDocument是获取XML文件对应的Document实例,详细可以参考这里<2021-02-24 16:02>,根据Document文档信息注册BeanDefinition信息,详细参考这里

猜你喜欢

转载自blog.csdn.net/wang0907/article/details/114016448