Analysis of Spring Parsing Import Tag Process

Write in front

This article continues the analysis on the basis of this article . This article mainly analyzes the process of importing configuration files with the import tag.

1: Role

Decoupling configuration files reduces the complexity of configuration file writing and later maintenance.

2: Test code

In order to facilitate debugging and then paste the test 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);
}

xml:

<?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 id="testBeanDefinitionBean1" class="yudaosourcecode.spring.TestBeanDefinitionBean"/>
    <import resource="needimported.xml"/>
</beans>

Introduced into the configuration file needimported.xml:

<?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="testBeanDefinitionBean2" class="yudaosourcecode.spring.TestBeanDefinitionBean"/>
</beans>

Then org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElementhit the following breakpoints to start debugging:
Insert picture description here

3:importBeanDefinitionResource

Source code:

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
protected void importBeanDefinitionResource(Element ele) {
    
    
	// 获取import标签的resource属性的值,即需要引入的配置文件的地址
	String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
	// 如果是resource值为空,直接error,并return
	if (!StringUtils.hasText(location)) {
    
    
		getReaderContext().error("Resource location must not be empty", ele);
		return;
	}
	// <2021-03-01 10:54>
	// 解析系统环境变量
	location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
	// 最终需要加载的资源的集合
	Set<Resource> actualResources = new LinkedHashSet<>(4);
	// 是相对路径还是绝对路径?
	boolean absoluteLocation = false;
	try {
    
    
		// 1:classpath:,classpath*:是绝对路径
		// 2:能够构造url的是绝对路径,如file:/xxxx,http://,jar:// 等资源访问协议定义的路径
		absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
	}
	catch (URISyntaxException ex) {
    
    
		...snip...
	}

	// 如果是绝对路径,classpath:,classpath*:,file:/,http:等
	// 这里需要区分是相对路径还是绝对路径的原因是,递归加载
	// import资源使用的方式和API会有所不同
	if (absoluteLocation) {
    
    
		try {
    
    
			// 递归导入,注意这里调用的是loadBeanDefinition的另一个重载版本了,不过殊途同归
			int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
			if (logger.isTraceEnabled()) {
    
    
				logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
			}
		}
		catch (BeanDefinitionStoreException ex) {
    
    
			getReaderContext().error(
					"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
		}
	}
	// 如果是相对路径,非以classpath:,classpath*:,http://.file:/等开头的路径
	// 如果不是绝对路径则默认就是相对于当前资源的相对路径
	else {
    
    
		try {
    
    
			int importCount;
			// 根据当前资源(当前解析的import标签所在资源)的相对路径创建需要import的资源
			Resource relativeResource = getReaderContext().getResource().createRelative(location);
			// 需要import的相对路径资源存在
			if (relativeResource.exists()) {
    
    
				// 递归调用
				importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
				// 添加资源
				actualResources.add(relativeResource);
			}
			// 需要import的相对路径资源不存在,理论上相对不存在,这种方式也不会存在,不知道为啥还要执行后续操作
			else {
    
    
				// 获取当前资源基础路径,如file:/E:/workspace-idea/java-life/target/classes/testbeandefinition.xml
				String baseLocation = getReaderContext().getResource().getURL().toString();
				// <2021-03-01 11:32>
				importCount = getReaderContext().getReader().loadBeanDefinitions(
						StringUtils.applyRelativePath(baseLocation, location), actualResources);
			}
			if (logger.isTraceEnabled()) {
    
    
				logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
			}
		}
		catch (IOException ex) {
    
    
			getReaderContext().error("Failed to resolve current resource location", ele, ex);
		}
		catch (BeanDefinitionStoreException ex) {
    
    
			getReaderContext().error(
					"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
		}
	}
	// 加载到的资源转成数据,并触发导入已经处理事件
	Resource[] actResArray = actualResources.toArray(new Resource[0]);
	getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

<2021-03-01 10:54>It is to replace environment variables, such as the following environment variables:

C:\Users\Administrator>set
ALLUSERSPROFILE=C:\ProgramData
...snip...

Then modify the xml to <import resource="${ALLUSERSPROFILE}/needimported.xml"/>debug there, and the obtained value is as follows:
Insert picture description here
You can see that the actual value of the environment variable has been replaced.
<2021-03-01 11:32>The point is to apply the relative path based on the base absolute path to obtain the absolute path of the relative path. If the base absolute path is the file:/E:/workspace-idea/java-life/target/classes/testbeandefinition.xmlrelative path needimported1.xml, the result is file:/E:/workspace-idea/java-life/target/classes/needimported1.xml.
Then let's look at the parsing process of the bean tag through here , because the bean tag is to be resolved in the end.

Guess you like

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