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 ofNamedThreadLocal
Yesjava.lang.ThreadLocal
The 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
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:
<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.loadDocument
It 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 .