Spring source code interpretation of "IOC container 2-Bean loading process"

In the last article, we implemented a simple IOC container. In this article, we will introduce the implementation of Spring IOC container.

1. Preparation

In order to learn the source code implementation of Spring, we need to prepare the source code environment of Spring. At this time, we generally have the following two choices:

1.1 download the spring framework git project

take Spring framework git project Download to local

git clone https://github.com/spring-projects/spring-framework.git

Import the source code into Idea

There is a problem with this method. Now the download speed of github is very slow, and the spring framework is very large, so the download generally fails. To solve this problem, you can modify the host file or hang an agent. I think that by hanging the agent, please refer to: https://www.zhihu.com/question/27159393

After importing the source code into Idea, the Idea will automatically Sync the project. When the Sync is finished (this process usually takes a long time), it indicates that the spring framework has been imported to the local area correctly. At this time, the project structure will look like the following:

1.2 introduce related jar packages through maven

In addition to the above methods, we can also introduce spring related jar packages into local projects through maven. For example, our project pom.xml Add spring context dependency to the file:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>

Then the spring context related packages can be imported into the project. However, Idea only imports the bytecode jar package by default, and does not download the sourceCode package, which is very inconvenient for us to view the source code. We can use the following settings to let Idea download the sourceCode package while downloading the jar package.

Preferences > build, execution, deployment > Build Tools > Maven > importing

 

Idea re import and download source code package:

2. Source code entry

When we try to study the Spring source code, we usually have no way to start at the beginning, because the overall hierarchical structure and implementation of Spring are relatively complex. And we usually only care about the configuration file (configuration class) level, rarely pay attention to the underlying implementation. First, let's take a look at Spring The entry of IOC source code reading. When we learn Spring again, we will definitely contact the following demo (here we use the demo of xml configuration file, and the Spring principle of Java configuration is the same, except that the entry class is AnnotationConfigApplicationContext. The process of loading Bean configuration as BeanDefinition is different, and the subsequent process is completely consistent.)

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
applicationContext.getBean(XXX.class);

ClassPathXmlApplicationContext is used to load the Spring configuration file under CLASSPATH. It can be seen that Bean instances can be obtained in the second line. Therefore, all Bean instances must be loaded in the first line (loading here indicates that the beans defined in the configuration file have been instantiated and initialized). Then the entry must be in the constructor of ClassPathXmlApplicationContext class, as follows:

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

In this constructor, another constructor is called, as follows:

public ClassPathXmlApplicationContext(
		String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
		throws BeansException {

	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}
  • super method: call the parent class constructor, where parent is null, indicating that there is no parent container
  • setConfigLocations method: used to replace the ${PlaceHolder} in the path of Spring configuration file specified by the parameter configLocations with the Value value corresponding to PlaceHolder in the system variable, and store it in the member variable configLocations, so that we can obtain the byte input stream of the configuration file before loading the Bean definition as BeanDefinition
  • Refresh method: the core method of Spring Bean loading. It is a method of AbstractApplicationContext, the parent class of ClassPathXmlApplicationContext. It is used to refresh the Spring container context information and define the loading process of Spring container Bean
@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 1. Prepare to refresh the Spring context, which is mainly used to record the start time of Spring context loading and set some basic member variables value
		prepareRefresh();

		// 2. Refresh BeanFactory. This method completes the transformation and registration from the configured Bean in the configuration file to BeanDefinition
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 3. Prepare Bean factory, which is mainly used to configure the basic information of BeanFactory, such as the context ClassLoader and postprocessor
		prepareBeanFactory(beanFactory);

		try {
			// 4. Allow sub context to add some beanfactory postprocessors,
			// For example, in the Web application, abstractrefreshable Web ApplicationContext is added with ServletContextAwareProcessor,
			// You can skip this method for the time being
			postProcessBeanFactory(beanFactory);

			// 5. Execute the method defined in beanfactory postprocessor
			invokeBeanFactoryPostProcessors(beanFactory);

			// 6. Register all beanpostprocessors. This part of beanpostprocessors will use the finishBeanFactoryInitialization method below
			// Use in process
			registerBeanPostProcessors(beanFactory);

			// 7. Initialize the message source, which is the interface defined by Spring for accessing internationalization
			initMessageSource();

			// 8. Initialize the context event broadcaster
			initApplicationEventMulticaster();

			// 9. Template method, which can be rewritten to add special context refresh work
			onRefresh();

			// 10. Register listener
			registerListeners();

			// 11. Instantiate all defined singleton beans
			finishBeanFactoryInitialization(beanFactory);

			// End Spring context refresh
			finishRefresh();
		}

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

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			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...
			resetCommonCaches();
		}
	}
}

The above process will be described in detail in the following article. Here we will simply list the steps of Bean loading. For us to understand Bean loading, it is important to step 2 and step 11, which define how the Bean changes from a configuration file to a usable object when Spring starts. The following article will introduce this part first, and then explain the remaining steps step by step. At the same time, for the refresh method, we need to pay attention to the following two points:

  • The body of the refresh method is locked synchronously. The lock is used in the close method to ensure that the close() method cannot be called when the refresh() method is called. Or the refresh() method cannot be called when the close method is called to ensure mutual exclusion.
  • In this method, many sub methods related to Bean loading are defined, and each sub method performs its own functions, which is very clear to read. Otherwise, the flowers will unify the logic scattered in the sub methods to the refresh method, which should have at least a few thousand lines. Therefore, the module partition method improves the scalability and maintainability of the code.

3. Inheritance system

Through the above simple logical carding, we can find that there are two more important classes in the process of Bean loading. They are the entry class ClassPathXmlApplicationContext and the member variable beanFactory obtained from the entry class through inheritance. You can find that the instance is DefaultListableBeanFactory in the code. Let's take a look at the inheritance system of these two classes.

 

By analogy with the IOC container we have previously customized, we can find that the functions of ClassPathXmlApplicationContext are basically the same as those of our previous custom implementation. It implements the BeanFactory interface, so it is a special BeanFactory. For ClassPathXmlApplicationContext, it should be noted that its inherited abstract class AbstractRefreshableApplicationContext has an instance of DefaultListableBeanFactory. The functions of ClassPathXmlApplicationContext that can realize Bean factory are implemented through the inherited member variable. The member variable is initialized in obtainFreshBeanFactory, a child of the above refresh method.

For the DefaultListableBeanFactory, we need to focus on that this class implements both BeanFactory interface and BeanDefinitionRegistry interface. It is a Bean factory and a Bean registry.

4. Bean loading process

Let's take a look at the Bean loading process, which is actually the general execution process of the refresh method.

4.1 prepareRefresh

This method is the method of AbstractApplicationContext, as follows:

/**
* Prepare this context for refreshing, setting its startup date and
* active flag as well as performing any initialization of property sources.
*/
protected void prepareRefresh() {
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);

	if (logger.isInfoEnabled()) {
		logger.info("Refreshing " + this);
	}

	// Initialize any placeholder property sources in the context environment
	initPropertySources();

	// Validate that all properties marked as required are resolvable
	// see ConfigurablePropertyResolver#setRequiredProperties
	getEnvironment().validateRequiredProperties();

	// Allow for the collection of early ApplicationEvents,
	// to be published once the multicaster is available...
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

As can be seen from the method annotation, the function of this method is to prepare for refreshing the context and set the Spring context loading start time. The rest is to set some member variables value, such as the closed flag and the active flag. It's very simple, and there's nothing special to say.

4.2 obtainFreshBeanFactory

This method is very important for us to understand the loading process of Spring Bean, which needs to be focused on. This method is the method of AbstractApplicationContext, which is used to obtain the Bean factory to refresh the Spring context. The return value of this method is the DefaultListableBeanFactory type member variable of the AbstractRefreshableApplicationContext class mentioned above. In this method, the transformation from xml configuration file to BeanDefinition is completed by using XMLBeanDefinitionReader, as follows:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (logger.isDebugEnabled()) {
		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
	}
	return beanFactory;
}

The object returned by this method is obtained through the getBeanFactory method, which is an abstract method in the AbstractApplicationContext class

public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

It is implemented in the subclass AbstractRefreshableApplicationContext, as follows:

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
	synchronized (this.beanFactoryMonitor) {
		if (this.beanFactory == null) {
			throw new IllegalStateException("BeanFactory not initialized or already closed - " +
					"call 'refresh' before accessing beans via the ApplicationContext");
		}
		return this.beanFactory;
	}
}

Method implementation actually returns the member variable instance of AbstractRefreshableApplicationContext. Therefore, before calling getBeanFactory() method above, the initialization of AbstractRefreshableApplicationContext member variable beanFactory must be completed. Take a look at the refreshBeanFactory method called before calling the getBeanFactory method:

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

It is also an abstract method of AbstractApplicationContext class, which is implemented in subclass AbstractRefreshableApplicationContext:

@Override
protected final void refreshBeanFactory() throws BeansException {
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		//1. Create a DefaultListableBeanFactory object instance
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		//2. Set serializationId
		beanFactory.setSerializationId(getId());
		//3. Customized settings beanFactory
		customizeBeanFactory(beanFactory);
		//4. Load the Bean configuration in the xml configuration file, convert it into BeanDefinition, and register it with BeanFactory in the registry
		loadBeanDefinitions(beanFactory);
		synchronized (this.beanFactoryMonitor) {
			this.beanFactory = beanFactory;
		}
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

4.3 prepareBeanFactory

prepareBeanFactory method is a method of AbstractApplicationContext class, which is used to configure the basic features of BeanFactory

/**
* Configure the basic features of BeanFactory, such as ClassLoader and post processor
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 1. Configure classLoader for beanFactory
	beanFactory.setBeanClassLoader(getClassLoader());
	// 2. Set the spEl expression for beanFactory as the language processor for parsing#{ bean.xxx }Property value set by
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
	// 3. Set the property editor for beanFactory
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// 4. Add ApplicationContextAwareProcessor to beanFactory for context callback
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

	// 5. Set the implementation class Bean of the following interfaces not to be automatically assembled
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

	// 6. Fix the dependency and set special rules for automatic assembly. For example, the implementation class of BeanFactory interface is modified to the current BeanFactory
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// 7. Add the post processor of ApplicationListenerDetector for beanFactory, which is used to register the bean that implements the ApplicationListener interface as a listener
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// 8. If a Bean named "loadTimeWeaver" has been defined in the custom Bean, a loadTimeWeaver awareprocessor will be added
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

	// 9. Register system environment information beans for beanFactory, such as "environment", "systemProperties", "systemEnvironment"
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}

4.4 invokeBeanFactoryPostProcessors

Invokebenfactory postprocessors is a method of AbstractApplicationContext that implements the methods defined in beanfactory postprocessor. Beanfactory postprocessor interface provides extension points for all operations before Bean instantiation. At the same time, it should be noted that before the Bean is instantiated, it means that before calling the initialization method of the Bean, the beanfactory postprocessor interface method is called before any custom Bean is instantiated and will only be executed once.

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	// Reflect the methods that execute all implementation classes of beanfactory postprocessor
	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

	// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
	// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
	if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}
}
public static void invokeBeanFactoryPostProcessors(
		ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

	// Invoke BeanDefinitionRegistryPostProcessors first, if any.
	Set<String> processedBeans = new HashSet<>();

	
	if (beanFactory instanceof BeanDefinitionRegistry) {
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
		List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
		List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

		// 1. The beandefinitionregistrypostprocessor interface inherits the beanfactory postprocessor interface,
		// Here, the postProcessBeanDefinitionRegistry method of the BeanDefinitionRegistryPostProcessor interface is executed
		// The beanfactory postprocessors parameter is obtained through getbeanfactory postprocessors(),
		// When you start the Spring container through the xml configuration file, the method returns null, so it does not enter the for loop
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				BeanDefinitionRegistryPostProcessor registryProcessor =
						(BeanDefinitionRegistryPostProcessor) postProcessor;
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				registryProcessors.add(registryProcessor);
			}
			else {
				regularPostProcessors.add(postProcessor);
			}
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		// Separate between BeanDefinitionRegistryPostProcessors that implement
		// PriorityOrdered, Ordered, and the rest.
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

		// 2. Collect all beandefinitionregistrypostprocessors that implement the PriorityOrdered interface, and execute the postProcessBeanDefinitionRegistry method
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				// Current registry processors are used to store a collection of beandefinitionregistrypostprocessors of some type currently being processed
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				// processedBeans is used to store the registered BeanDefinitionRegistryPostProcessor names, which are used to exclude the implementation of the PriorityOrdered interface
				// And beandefinitionregistry postprocessor, which implements the Ordered interface
				processedBeans.add(ppName);
			}
		}
		//sort
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		// Adds the BeanDefinitionRegistryPostProcessor of the current type to the registered container
		// The postProcessBeanFactory method used to execute BeanDefinitionRegistryPostProcessor below
		registryProcessors.addAll(currentRegistryProcessors);
		// Execute the postProcessBeanDefinitionRegistry method of BeanDefinitionRegistryPostProcessor
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		// Empty the current registry processors container, which will continue to process other types of beandefinitionregistrypostprocessors
		currentRegistryProcessors.clear();

		// 3. Collect all beandefinitionregistrypostprocessors that implement the Ordered interface and execute the postProcessBeanDefinitionRegistry method
		postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
		for (String ppName : postProcessorNames) {
			if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		// 4. Collect the BeanDefinitionRegistryPostProcessor which implements the PriorityOrdered interface and Ordered interface, and execute the postProcessBeanDefinitionRegistry method
		boolean reiterate = true;
		while (reiterate) {
			reiterate = false;
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (!processedBeans.contains(ppName)) {
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
					reiterate = true;
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();
		}

		// 5. Execute the postProcessBeanFactory method of BeanDefinitionRegistryPostProcessor
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	}

	else {
		// Invoke factory processors registered with the context instance.
		invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
	}

	// 6. Process all beanfactory postprocessors. The logic is similar to that of beandefinitionregistry postprocessor
	String[] postProcessorNames =
			beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

	// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
	// Ordered, and the rest.
	List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (processedBeans.contains(ppName)) {
			// skip - already processed in first phase above
		}
		else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
		}
		else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		}
		else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}

	// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

	// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
	List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
	for (String postProcessorName : orderedPostProcessorNames) {
		orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

	// Finally, invoke all other BeanFactoryPostProcessors.
	List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
	for (String postProcessorName : nonOrderedPostProcessorNames) {
		nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

	// Clear cached merged bean definitions since the post-processors might have
	// modified the original metadata, e.g. replacing placeholders in values...
	beanFactory.clearMetadataCache();
}

4.5 registerBeanPostProcessors

The registerBeanPostProcessors method is defined in the AbstractApplicationContext class and is used to register beanpostprocessors. BeanPostProcessor provides an extension point for each Bean before and after initialization. Note that the register beanpostprocessors method is used to register the BeanPostProcessor and does not actually execute the pre - and post methods defined in BeanPostProcessor.

/**
* Instantiate and invoke all registered BeanPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
	PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
		ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

	// 1. Get the BeanName of all BeanPostProcessor types
	String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

	// Register BeanPostProcessorChecker that logs an info message when
	// a bean is created during BeanPostProcessor instantiation, i.e. when
	// a bean is not eligible for getting processed by all BeanPostProcessors.
	int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
	beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

	// 2. All beanpostprocessors are divided into three types: the implementation of PriorityOrdered interface, the implementation of Ordered interface and other three types
	List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			priorityOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		}
		else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}

	// 3. Register all beanpostprocessors that implement the PriorityOrdered interface after sorting
	// The registration here is to add BeanPostProcessor to beanPostProcessors, a member variable of beanFactory
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

	// 4. Register all beanpostprocessors that implement the Ordered interface after sorting
	List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
	for (String ppName : orderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		orderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, orderedPostProcessors);

	// 5. Register all beanpostprocessors except the above two beanpostprocessors
	List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
	for (String ppName : nonOrderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		nonOrderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

	// 6. Register all MergedBeanDefinitionPostProcessor instances after sorting
	sortPostProcessors(internalPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, internalPostProcessors);

	// 7. Register ApplicationListenerDetector
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

4.6 initMessageSource

The initmessage source method is defined in the AbstractApplicationContext class, which is used to initialize the message source. The message source is the interface defined by Spring to realize the access internationalization.

/**
* Initialize the MessageSource.
* Use parent's if none defined in this context.
*/
protected void initMessageSource() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	// If a bean named "message source" is customized, the bean is instantiated
	if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
		this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
		// Make MessageSource aware of parent MessageSource.
		if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
			HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
			if (hms.getParentMessageSource() == null) {
				// Only set parent context as parent MessageSource if no parent MessageSource
				// registered already.
				hms.setParentMessageSource(getInternalParentMessageSource());
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Using MessageSource [" + this.messageSource + "]");
		}
	}
	// Otherwise, a delegatingmessage source instance is generated by default and registered with beanFactory
	else {
		// Use empty MessageSource to be able to accept getMessage calls.
		DelegatingMessageSource dms = new DelegatingMessageSource();
		dms.setParentMessageSource(getInternalParentMessageSource());
		this.messageSource = dms;
		beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
		if (logger.isDebugEnabled()) {
			logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
					"': using default [" + this.messageSource + "]");
		}
	}
}

4.7 initApplicationEventMulticaster

The initApplicationEventMulticaster method is defined in the AbstractApplicationContext class to initialize the Spring container broadcaster.

/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	// If a Bean named "application event multicast" is customized, the Bean is instantiated and registered with the container
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
		this.applicationEventMulticaster =
				beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
		if (logger.isDebugEnabled()) {
			logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
		}
	}
	// Otherwise, a simpleapplicationeventmulticast instance is generated and registered with the container
	else {
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
		if (logger.isDebugEnabled()) {
			logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
					APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
					"': using default [" + this.applicationEventMulticaster + "]");
		}
	}
}

4.8 onRefresh

The onRefresh method is defined in the AbstractApplicationContext class, which is a template method. This method can be overridden to add a special context refresh for the container, which is called when and before the initialization of the special Bean. For example, abstractrefreshable webapplicationcontext and StaticWebApplicationContext both override this method.

/**
* Template method which can be overridden to add context-specific refresh work.
* Called on initialization of special beans, before instantiation of singletons.
* <p>This implementation is empty.
* @throws BeansException in case of errors
* @see #refresh()
*/
protected void onRefresh() throws BeansException {
	// For subclasses: do nothing by default.
}

4.9 registerListeners

The registerListeners method is defined in the AbstractApplicationContext class and is used to register listeners.

/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
	// Register statically specified listeners first.
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// Do not initialize FactoryBeans here: We need to leave all regular beans
	// uninitialized to let post-processors apply to them!
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// Publish early application events now that we finally have a multicaster...
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (earlyEventsToProcess != null) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}

4.10 finishBeanFactoryInitialization

The finishBeanFactoryInitialization method is the most important step in the Spring container Bean loading process. All non lazy loaded beans will be instantiated and initialized in this method. We will introduce this method separately in the following article.

4.11 finishRefresh

The finishRefresh method is defined in the AbstractApplicationContext class, which is used to end the Spring context refresh. It is not so "important" for us to understand that the Spring container Bean loading is not so "important", so we can choose to skip it. Here we will not discuss it in depth.

/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
	// Clear context-level resource caches (such as ASM metadata from scanning).
	clearResourceCaches();

	// Initialize lifecycle processor for this context.
	initLifecycleProcessor();

	// Propagate refresh to lifecycle processor first.
	getLifecycleProcessor().onRefresh();

	// Publish the final event.
	publishEvent(new ContextRefreshedEvent(this));

	// Participate in LiveBeansView MBean, if active.
	LiveBeansView.registerApplicationContext(this);
}

Reference link:

1. Spring source code

2. Some operations before and after initialization of non lazy loaded singleton beans

Keywords: Spring xml git Maven

Added by djg_uk on Tue, 30 Jun 2020 09:33:14 +0300