Spring configuration file is loaded as BeanDefinition process analysis

1: write in front

In actual work, I may often write the following code:

@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);
}

The content of the configuration file used is as follows:

<?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>

This code is to load the bean information in the configuration file as the BeanDefinition in spring, which is the data structure that stores the bean information we defined in spring. The final generation of spring beans also depends on this data structure. The final running result of the code is as follows:

bean定义的个数是:2

What is returned is the number of beans defined in the configuration file.

2:loadBeanDefinitions

This method completes the function of loading beandefinition from the resource encapsulated with the configuration file, the specific location is as follows:

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

carry on:

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>The variable is defined as private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>("XML bean definition resources currently being loaded");a subclass NamedThreadLocalof java.lang.ThreadLocalthe class, but the attribute of the name is added. The source code is as follows:

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>The point is to judge whether the resource exists, mainly to deal with the situation of introducing oneself, so that an infinite loop of resource introduction will occur, so deal with it, the following situation will be an infinite loop:
Insert picture description here
test and run the following code:

@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);
}

Abnormal operation: the
Insert picture description here
<2021-02-24 12:27>source code is as follows:

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>The source code is as follows:

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

The code getValidationModeForResource(resource)is to obtain the verification model, you can refer to here for details . The code this.documentLoader.loadDocumentis to obtain the Document instance corresponding to the XML file. For details, please refer to here . <2021-02-24 16:02>, Register BeanDefinition information according to Document document information, refer to here for details .

Guess you like

Origin blog.csdn.net/wang0907/article/details/114016448