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 import tags importing configuration files.

1: role

Decouple configuration files to reduce the complexity of configuration file writing and post-maintenance.

2: Test code

For the convenience of debugging, paste the test code:

@Test 
public  void  testBeanDefinitionLoad ( )  { 
    // Define resource 
    ClassPathResource classPathResource =  new  ClassPathResource ( "testbeandefinition.xml" ) ; 
    // Define IOC container 
    DefaultListableBeanFactory defaultListableBeanFactory =  new  DefaultListableBeanFactory ( ) ; 
    // Define bean definition reader 
    XmlBeanDefinitionReader xmlBeanDefinitionReader =  new  XmlBeanDefinitionReader ( defaultListableBeanFactory ) ; // Read bean definition 
    int from resource through bean definition reader
    i = xmlBeanDefinitionReader . loadBeanDefinitions ( classPathResource ) ; 
    System . out . println ( "The number of bean definitions is: "  + i ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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 >
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

imported into the configuration fileneedimported.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 >
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

then inorg.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElementHit the following breakpoint to start debugging:
insert image description here

3: importBeanDefinitionResource

Source code:

org . springframework . beans . factory . xml . DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
 protected  void  importBeanDefinitionResource ( Element ele )  { 
	// Get the value of the resource attribute of the import tag, that is, the address of the configuration file to be imported 
	String location = ele . getAttribute ( RESOURCE_ATTRIBUTE ) ; 
	// If the resource value is empty, error directly, and return 
	if  ( ! StringUtils . hasText ( location ) )  {
		getReaderContext ( ) . error ( "Resource location must not be empty" , ele ) ; 
		return ; 
	} 
	// <2021-03-01 10:54> 
	// resolve system environment variables 
	location =  getReaderContext ( ) . getEnvironment ( ) . resolveRequiredPlaceholders ( location ) ; 
	// A collection of resources to be loaded in the end 
	Set < Resource > actualResources =  new  LinkedHashSet < > ( 4 ); 
	// Is it a relative path or an absolute path? 
	boolean absoluteLocation =  false ; 
	try  { 
		// 1:classpath:,classpath*: is an absolute path 
		// 2: It is an absolute path that can construct a url, such as file:/xxxx, http://, jar:// and other resource access Protocol defined path 
		absoluteLocation = ResourcePatternUtils . isUrl ( location )  || ResourceUtils . toURI ( location ) . isAbsolute ( ) ; 
	} 
	catch  ( URISyntaxException ex )  { 
		. . . snip . .. 
	}

	// If it is an absolute path, classpath:, classpath*:, file:/, http: etc. 
	// The reason why we need to distinguish between relative paths and absolute paths is that recursive loading 
	// The way and API of import resources will be different. Different 
	if  ( absoluteLocation )  { 
		try  { 
			// Recursive import, note that another overloaded version of loadBeanDefinition is called here, but 
			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 ) ; 
		} 
	} 
	// If it is a relative path, not a path starting with classpath:, classpath*:, http://.file:/, etc. 
	// If it is not an absolute path, the default is relative relative path to the current resource
	else  { 
		try  { 
			int importCount ; 
			// Create a resource that needs to be imported based on the relative path of the current resource (the resource where the currently parsed import tag is located) 
			Resource relativeResource =  getReaderContext ( ) . getResource ( ) . createRelative ( location ) ; 
			// needs to be imported relative path resource exists 
			if  ( relativeResource . exists ( ) )  { 
				// recursively call 
				importCount =  getReaderContext ( ) . getReader ( ) .loadBeanDefinitions ( relativeResource ) ; 
				// Add resources 
				actualResources . add ( relativeResource ) ; 
			} 
			// The relative path resource that needs to be imported does not exist, theoretically it does not exist, and this method does not exist. I don't know why it is necessary to execute the follow-up Operation 
			else  { 
				// Get the current resource base path, such as 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 ); 
		} 
	} 
	// The loaded resource is converted into data and triggers the import processed event 
	Resource [ ] actResArray = actualResources . toArray ( new  Resource [ 0 ] ) ; 
	getReaderContext ( ) . fireImportProcessed ( location , actResArray ,  extractSource ( ele ) ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79

<2021-03-01 10:54>Is to replace environment variables, for example, there are the following environment variables:

C:\Users\Administrator>set
ALLUSERSPROFILE=C:\ProgramData
...snip...
  • 1
  • 2
  • 3

Then modify the xml to<import resource="${ALLUSERSPROFILE}/needimported.xml"/>, debug there, and the obtained values ​​are as follows:
insert image 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 according to the base absolute path, and obtain the absolute path of the relative path. For example, the base absolute path isfile:/E:/workspace-idea/java-life/target/classes/testbeandefinition.xml, the relative path isneedimported1.xml, then the result isfile:/E:/workspace-idea/java-life/target/classes/needimported1.xml.
Next, let's take a look at the parsing process of the bean tag through here , because in the end, the bean tag is to be parsed.

Tags: Analysis of spring parsing import tag process

spring java spring

Related: Analysis of spring parsing import tag process