Spring configuration file is loaded as BeanDefinition process analysis

1: write in front

In real work, I may often write code like this:

@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

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 >
     <!-- If you import yourself here, an org.springframework.beans.factory.BeanDefinitionStoreException will occur-->
    <!--<import resource="testbeandefinition.xml"/>-->
 </ beans >
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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 result of the code is as follows:

The number of bean definitions is: 2
  • 1

What is returned is the number of beans we 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 { 
	// Where EncodedResource is an encoded resource, it does not affect the main Process, you can ignore 
	return  loadBeanDefinitions ( new  EncodedResource ( resource )) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

continue:

org . springframework . beans . factory . xml . XmlBeanDefinitionReader# loadBeanDefinitions ( org . springframework . core . io . support . EncodedResource ) 
public  int  loadBeanDefinitions ( EncodedResource encodedResource )  throws BeanDefinitionStoreException { 
	// Assert that the incoming resource is not 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> 
	// from resourcesCurrentlyBeingLoaded Get the resource collection that the current thread has or is loading in 
	Set < EncodedResource > currentResources =  this . resourcesCurrentlyBeingLoaded . get ( ) ; 
	// If there is no loaded resource, create it, note that this is
	// HashSet used because resources are not allowed to be duplicated 
	if  ( currentResources == null )  { 
		currentResources =  new  HashSet < > ( 4 ) ; 
		// store newly created resources in resourcesCurrentlyBeingLoaded 
		this . resourcesCurrentlyBeingLoaded . set ( currentResources ) ; 
	} 
	/ / <2021-02-24 12:23> 
	// Here, if the resource already exists in the set collection, it will return false, which means the resource is duplicated . 
	// Throw BeanDefinitionStoreException information directly 
	if  ( ! currentResources . add ( encodedResource ))  { 
		throw  new  BeanDefinitionStoreException ( 
				"Detected cyclic loading of "  + encodedResource +  " - check your import definitions!" ) ; 
	} 
	try  { 
		// Get the corresponding resource, then get the input stream from the resource 
		InputStream inputStream = encodedResource . getResource ( ) .getInputStream ( ) ; try { // encapsulate in InputSource InputSource 
			inputSource = new InputSource ( inputStream ) ;
		 
			  
			// If there is encoding, use it, it does not affect the main process, you can ignore 
			if  ( encodedResource . getEncoding ( )  != null )  { 
				inputSource . setEncoding ( encodedResource . getEncoding ( ) ) ; 
			} 
			// <2021-02-24 12:27 > 
			// The process of implementing concrete loading as BeanDefinition 
			return  doLoadBeanDefinitions ( inputSource , encodedResource . getResource ( ) ) ; 
		} 
		finally  { 
			inputStream . close( ) ; 
		} 
	} 
	catch  ( IOException ex )  { 
		throw  new  BeanDefinitionStoreException ( 
				"IOException parsing XML document from "  + encodedResource . getResource ( ) , ex ) ; 
	} 
	finally  { 
		// remove resource 
		currentResources . remove ( encodedResource ) ; 
		// if removed After the current resource, if there are no other resources, then clear the threadlocal 
		if  ( currentResources . isEmpty ( ))  { 
			this . resourcesCurrentlyBeingLoaded . remove ( ) ; 
		} 
	} 
}
  • 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

<2021-02-24 12:21>The variable is defined asprivate final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<>("XML bean definition resources currently being loaded");the class ofNamedThreadLocalYesjava.lang.ThreadLocalThe subclass of , just adds the attribute of the name, 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 ; 
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

<2021-02-24 12:23>The place is to judge whether the resource exists, mainly to deal with the situation that you introduce yourself, this will cause an infinite loop of resource introduction, so deal with it, the following situation will be an infinite loop: The
insert image description here
following code is tested and run:

@Test 
public  void  testBeanDefinitionStoreException ( )  { 
    // 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 definitions from resources via the bean definition reader
    xmlBeanDefinitionReader . loadBeanDefinitions ( classPathResource ) ; 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Exception running:
insert image description here
<2021-02-24 12:27>The 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> Get the document object corresponding to the xml file 
		Document doc =  doLoadDocument ( inputSource , resource ) ; 
		// <2021-02-24 16:02> Register Bean information according to the document object 
		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  ( Throwableex )  { 
		. . . snip . . . 
	} 
}
  • 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

<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 ( ) ) ; 
}
  • 1
  • 2
  • 3
  • 4

codegetValidationModeForResource(resource)It is to obtain the verification model. For details, please refer to here . codethis.documentLoader.loadDocumentIt is 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 the Document document information, refer to here for details .

Tags: Spring configuration file is loaded as BeanDefinition process analysis

spring spring java

Related: Spring configuration file is loaded as BeanDefinition process analysis