Initialization loading of IOC container based on Xml

Posted by seavers on Fri, 25 Feb 2022 15:45:14 +0100

The following is the initialization loading of IOC container based on Xml. Please refer to the previous article for the positioning of the container Initialization of IOC container based on Xml (I) positioning

3. Start

The spring IOC container loads Bean configuration resources from the refresh () function. Refresh () is a template method that specifies the startup process of the IOC container, and some logic needs to be implemented by its subclasses. It loads Bean configuration resources. ClassPathXmlApplicationContext starts the loading process of Bean definitions by calling the refresh() function of its parent class AbstractApplicationContext. Now let's take a detailed look at the logic processing in refresh():

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1. Call the method that the container is ready to refresh, obtain the current time of the container, and set the synchronization ID at the same time
			prepareRefresh();

			// Tell the subclass to start the refreshBeanFactory() method and load the Bean definition resource file from
			//The subclass's refreshBeanFactory() method starts
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//3. Configure container features for BeanFactory, such as class loader, event handler, and so on
			prepareBeanFactory(beanFactory);

			try {
				//4. Specify a special BeanPost event handler for some subclasses of duration
				postProcessBeanFactory(beanFactory);

				//5. All postbean registrations of factorybeanprocessor
				invokeBeanFactoryPostProcessors(beanFactory);

				//6. Register the BeanPost time processor for BeanFactory.
                //BeanPostProcessor is a Bean post processor, which is used to listen for container trigger events
				registerBeanPostProcessors(beanFactory);

				//7. Initializing the information source is related to internationalization
				initMessageSource();

				//8. Initialize container event propagator
				initApplicationEventMulticaster();

				//9. Call some special Bean initialization methods of subclasses
				onRefresh();

				//10. Register event listeners for event propagators
				registerListeners();

				//11. Initialize all remaining singleton beans
				finishBeanFactoryInitialization(beanFactory);

				//12. Initialize the container's lifecycle event handler and publish the container's lifecycle events
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				//13. Destroy the created Bean
				destroyBeans();

				//14. Cancel the refresh operation and reset the synchronization ID of the container
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
                //Reset public cache
				resetCommonCaches();
			}
		}
	}
}

The refresh() method mainly provides conditions for the life cycle management of IOC container beans. The Spring IOC container loads Bean configuration information from the refreshBeanFactory() method of its subclass container, so ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); The code after this sentence is the information source and life cycle events of the registration container. The loading we mentioned earlier starts from this code.

The main function of refresh() method is to destroy and close the existing IOC container if it already exists before creating the IOC container, so as to ensure that the newly established IOC container is used after refresh. It is similar to restarting the IOC container, initializing the container in the newly established container and loading the Bean configuration resources.

4. Create container

obtainFreshBeanFactory() method calls the refreshBeanFactory() method of the subclass container to start the process of loading Bean configuration information into the container. The code is as follows:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    //The method is only defined here, not implemented
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		//The delegation design pattern is used here. The parent class defines the abstract refreshBeanFactory() method, which specifically calls the refreshBeanFactory() method of the subclass container
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
}

The AbstractApplicationContext class only abstractly defines the refreshBeanFactory() method. What the container really calls is the refreshBeanFactory() method implemented by its subclass AbstractRefreshableApplicationContext. The source code of the method is as follows:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		//If there is already a container, destroy the bean s in the container and close the container
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			//Create IOC container
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			//Customize IOC containers, such as setting startup parameters, opening automatic assembly of annotations, etc
			customizeBeanFactory(beanFactory);
			//Calling the method defined by the load Bean mainly uses a delegation mode. In the current class, only the abstract loadBeanDefinitions method is defined, and the specific implementation calls the subclass container
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
}

In this method, first judge whether the beanFactory exists. If so, first destroy the beans and close the beanFactory, then create the DefaultListableBeanFactory, and call loadbean definitions (beanFactory) to load the bean definition.

5. Load configuration path

AbstractRefreshableApplicationContext class only defines the abstract loadBeanDefinitions(beanFactory) method, which actually calls AbstractXmlApplicationContext

loadBeanDefinitions(beanFactory) method. The source code of this method is as follows:

//Notice that the AbstractRefreshableApplicationContext class is inherited here
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

	//Load Bean definition method to implement parent class abstraction
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		//Create an XmlBeanDefinitionReader, that is, create a Bean reader and set it to the container through a callback. The container uses the reader to read the Bean definition resources
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		//Set the Spring resource loader for the Bean reader, AbstractXmlApplicationContext
		//The ancestor parent class AbstractApplicationContext inherits DefaultResourceLoader, so the container itself is also a resource loader
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		//Set up SAX xml parser for Bean reader
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//When the Bean reader reads the Xml resource file defined by the Bean, enable the Xml verification mechanism
		initBeanDefinitionReader(beanDefinitionReader);
		//How to load the Bean reader
		loadBeanDefinitions(beanDefinitionReader);
	}
    
    //The Xml Bean reader loads the Bean definition resource
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		//Gets the location of the Bean definition resource
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			//The Xml Bean reader calls its parent class AbstractBeanDefinitionReader to read the location
			//Bean definition resource for
			reader.loadBeanDefinitions(configResources);
		}
		//If the Bean definition resource obtained in the subclass is empty,
		//Gets the resource set by the setConfigLocations method in the FileSystemXmlApplicationContext constructor
        //Is the implementation of the Bean resource definition here!
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			//The Xml Bean reader calls its parent class AbstractBeanDefinitionReader to read the located Bean definition resource
			reader.loadBeanDefinitions(configLocations);
		}
	}
    
    //Here, a delegate mode is used to call the subclass's method to obtain the Bean definition resource location
	//This method is implemented in ClassPathXmlApplicationContext. For us
	//For example, the FileSystemXmlApplicationContext of the source code does not use this method (null is returned)
	@Nullable
	protected Resource[] getConfigResources() {
		return null;
	}
    
}

Take XmlBeanDefinitionReader, one of the policies of XmlBean reader, as an example. XmlBeanDefinitionReader calls the reader of its parent class AbstractBeanDefinitionReader The loadbeandefinitions() method reads the Bean configuration resources.

Since we use ClassPathXmlApplicationContext as an example for analysis, the return value of getConfigResources is null, so the program executes reader Loadbean definitions (configlocations) branch.

6. Allocation path processing strategy

The loading process is defined in the abstract parent class AbstractBeanDefinitionReader of XmlBeanDefinitionReader.

The source code of loadBeanDefinitions() method of AbstractBeanDefinitionReader is as follows:

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
    
    //Overload the method and call the following loadbean definitions (string, set < resource >); method
    //Execution sequence 2
	@Override
	public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(location, null);
	}
    
    //Execution sequence 3
    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) 
        throws BeanDefinitionStoreException {
		//Gets the resource loader set during IoC container initialization
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				//Resolve the Bean definition resource file at the specified location to the resource encapsulated by the Spring IOC container
				//Load Bean definition resource files at multiple specified locations
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				//Delegate to call the method of its subclass XmlBeanDefinitionReader to realize the loading function
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			//Resolve the Bean definition resource file at the specified location to the resource encapsulated by the Spring IOC container
			//Load the Bean definition resource file of a single specified location
			Resource resource = resourceLoader.getResource(location);
			//Delegate to call the method of its subclass XmlBeanDefinitionReader to realize the loading function
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
    
    //Overload method, call loadbean definitions (string);
    //Reader in AbstractXmlApplicationContextd class in point 5 loadBeanDefinitions(configLocations); That's it
    //Execution sequence 1
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int counter = 0;
		for (String location : locations) {
			counter += loadBeanDefinitions(location);
		}
		return counter;
	}

}

The loadBeanDefinitions(DefaultListableBeanFactory beanFactory) method of creating the container AbstractRefreshableApplicationContext in step 4 actually calls the loadBeanDefinitions() method of AbstractBeanDefinitionReader.

From the source code analysis of loadBeanDefinitions() method of AbstractBeanDefinitionReader, we can see that this method does two things:

  • First, call the resource loader method resourceloader Getresource (location) to get the resource to be loaded.
  • Secondly, the real loading function is the loadBeanDefinitions() method of its subclass XmlBeanDefinitionReader..

In the loadBeanDefinitions() method of AbstractBeanDefinitionReader, the getResources() method of AbstractApplicationContext is invoked (because ResourceLoader is implemented by AbstractApplicationContext). After entering it, it is found that the getResources() method is actually defined in ResourcePatternResolver. It is necessary to take a look at the complete class diagram of resourcepatternresolver:

From the above, we can see the inheritance relationship between ResourceLoader and ApplicationContext. We can see that calling the getResources() method of AbstractApplicationContext actually calls the getSource() method in DefaultResourceLoader to locate the Resource, because ClassPathXmlApplicationContext itself is the implementation class of DefaultResourceLoader, So now we return to ClassPathXmlApplicationContext.

Topics: Java xml Container