Spring source code - container refresh #invokeBeanFactoryPostProcessors()

Posted by littlevisuals on Sun, 09 Jan 2022 06:24:46 +0100

#Spring source code - container refresh #invokeBeanFactoryPostProcessors()

Spring version: Spring 5.3.13-release

#1. invokeBeanFactoryPostProcessors() activates the factory's processor into the IOC container as a Bean

The purpose of this step is to call the enhanced processors of various beanfactories. The most critical one is the ConfigurationClassPostProcessor. This enhanced processor completes the parsing of the configuration class and will parse the Bean definition of the Bean to be registered.

Abstractapplicationcontext#invokebeanfactoryprocessors() Code:

	/**
	 * Instantiate and register all beanfactoryprocessor bean instances according to the order
	 * 
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
	 * respecting explicit order if given.
	 * <p>Must be called before singleton instantiation.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		// Getbeanfactoryprocessors() directly returns the processor collection of beanfactoryprocessor type registered in hard coded form
		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)
		// Woven through class loader, loadTimeWeaver (LOAD_TIME_WEAVER_BEAN_NAME)
		if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
	}

The main code for registering beanfactoryprocessor is:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

Let's take a look at what getbeanfactoryprocessors() gets. Abstractapplicationcontext#getbeanfactoryprocessors() Code:

	@Override
	public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
		Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
		this.beanFactoryPostProcessors.add(postProcessor);
	}

	/**
	 * Returns the beanfactoryprocessor collection for the internal BeanFactory added through the addbeanfactoryprocessor() method
	 * @see AbstractApplicationContext#addBeanFactoryPostProcessor(BeanFactoryPostProcessor)
	 *
	 * Return the list of BeanFactoryPostProcessors that will get applied
	 * to the internal BeanFactory.
	 */
	public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
		// Directly return to beanfactoryprocessor
		return this.beanFactoryPostProcessors;
	}

You can see that getbeanfactoryprocessors () directly returns the beanfactoryprocessor collection used for the internal BeanFactory.

This sentence is very simple. The parameters passed here are the internal BeanFactory and all beanfactoryprocessors acting on the internal BeanFactory:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

#2. BeanFactory enhanced processor

Before looking at the specific implementation code of invokebeanfactoryprocessors(), let's talk about the enhanced processor of BeanFactory:

#1. Beanfactoryprocessor interface

Beanfactoryprocessor Code:

@FunctionalInterface
public interface BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}
     Modify the application context's internal bean factory after its standard
     initialization. All bean definitions will have been loaded, but no beans
     will have been instantiated yet. 

It can be seen from the official interface notes:

  • You can modify the BeanFactory inside the container after standard initialization. At this time, all beandefinitions have been loaded, but the Bean has not started instantiation.

Therefore, the Spring official annotation document has clearly explained the usefulness of this interface:

  • After all beandefinitions are loaded and before the Bean is instantiated, do some customization operations on the BeanDefinition, such as modifying the BeanDefinition of a Bean.

#2. Beandefinitionregistrypostprocessor interface:

BeanDefinitionRegistryPostProcessor Code:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

	/**
	 * Modify the application context's internal bean definition registry after its
	 * standard initialization. All regular bean definitions will have been loaded,
	 * but no beans will have been instantiated yet. This allows for adding further
	 * bean definitions before the next post-processing phase kicks in.
	 * @param registry the bean definition registry used by the application context
	 * @throws org.springframework.beans.BeansException in case of errors
	 */
	void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

You can see that BeanDefinitionRegistryPostProcessor is a sub interface of beanfactoryprocessor, so look at the class diagram first:

	 Modify the application context's internal bean definition registry after its
	 standard initialization. All regular bean definitions will have been loaded,
	 but no beans will have been instantiated yet. This allows for adding further
	 bean definitions before the next post-processing phase kicks in.

It can be seen from the official interface notes:

  • You can modify the BeanFactory inside the container after standard initialization. At this time, all conventional beandefinitions have been loaded, but the Bean has not started instantiation. This allows you to continue adding the Bean's BeanDefinition before the next Psot Processing phase.

Therefore, the Spring official annotation document has clearly explained the usefulness of this interface:

  • After all conventional beandefinitions are loaded, and before the Bean is instantiated, continue to add the beandefinition of the Bean before the next Post Processing stage.

#3. Execution sequence of beandefinitionregistrypostprocessor & beanfactoryprocessor interface

From this, we can also infer the execution order of the BeanDefinitionRegistryPostProcessor interface and beanfactoryprocessor interface. Bean definitionregistrypostprocessor must be executed prior to beanfactoryprocessor. The official annotation document has made it very clear that beanfactoryprocessor is before all beandefinitions are loaded and beans are instantiated.

#4. Beanfactoryprocessor injection method

There are two injection methods for beanfactoryprocessor interface:

  • Configuration injection: dynamically inject into the container through XML configuration file or configuration class.
  • Hard coding injection: directly call the abstractapplicationcontext#addbeanfactoryprocessor (beanfactoryprocessor postprocessor) method to add the beanfactoryprocessor to be injected into the abstractapplicationcontext#beanfactoryprocessors collection.

Beanfactoryprocoessor injected through hard coding does not need and does not support interface sorting, because it is added in order. For those injected through the above configuration, because Spring cannot guarantee the loading order, the beanfactoryprocessor injected through the configuration supports the sorting of PriorityOrdered and Ordered sorting interfaces.

3. Concrete implementation of invokeBeanFactoryPostProcessors()

From the above description, we know:

  • BeanDefinitionRegistryPostProcessor is a sub interface of BeanFactoryPostProcessor. BeanFactory of BeanDefinitionRegistry type is required in the BeanDefinitionRegistryPostProcessor sub interface, and BeanFactory of non BeanDefinitionRegistry type is activated by the BeanFactory postprocessor parent interface.

  • Beanfactoryprocessor can be injected in two ways: configuration and hard coding. At the same time, configuration injection supports interface sorting, while hard coded injection does not need interface sorting.

In the following source code, Spring implements the specific activation code of beanfactoryprocessor around the above two points.

The specific implementation of invokeBeanFactoryPostProcessors() is the PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors() method.

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

		// WARNING: Although it may appear that the body of this method can be easily
		// refactored to avoid the use of multiple loops and multiple lists, the use
		// of multiple lists and multiple passes over the names of processors is
		// intentional. We must ensure that we honor the contracts for PriorityOrdered
		// and Ordered processors. Specifically, we must NOT cause processors to be
		// instantiated (via getBean() invocations) or registered in the ApplicationContext
		// in the wrong order.
		//
		// Before submitting a pull request (PR) to change this method, please review the
		// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
		// to ensure that your proposal does not result in a breaking change:
		// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22

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

		// The processing of BeanDefinitionRegistry type is handled by BeanDefinitionRegistryPostProcessor
		// Judge the type of BeanFactory. If it is a subclass of BeanDefinitionRegistry,
		// It will be handled by BeanDefinitionRegistryPostProcessor, otherwise it will be handled directly by beanfactoryprocessor
		// Since BeanDefinitionRegistryPostProcessor can only process subclasses of BeanDefinitionRegistry, beanFactory must be distinguished here
		if (beanFactory instanceof BeanDefinitionRegistry) {
			// The following logic seems complex, but it is basically two steps
			// 1. Get all hard coded BeanDefinitionRegistryPostProcessor types and activate the postProcessBeanDefinitionRegistry method
			// 2. Obtain all configured BeanDefinitionRegistryPostProcessor types and activate the postProcessBeanDefinitionRegistry method

			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

			// Record the beanfactoryprocessor type processor that is hard coded
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			// Records the BeanDefinitionRegistryPostProcessor type processor that is hard coded
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
			// Traverse all hard coded registered beanfactoryprocessor processors
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				// If it is a processor of type BeanDefinitionRegistryPostProcessor
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					// Cast BeanFactoryPostProcessor type to BeanDefinitionRegistryPostProcessor
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					// Activate (call) BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry method
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					// Save it to registryProcessors
					registryProcessors.add(registryProcessor);
				}
				else {
					// Save hard coded processor injection objects of non BeanDefinitionRegistryPostProcessor type directly to regularPostProcessors
					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.
			// The processor used to record the BeanDefinitionRegistryPostProcessor type registered by configuration
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			// Gets the BeanName of all beandefinitionregistrypostprocessors registered through configuration
			String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			// Traverse the beannames of all beandefinitionregistrypostprocessors registered through configuration
			for (String ppName : postProcessorNames) {
				// Filter out the implementation classes of the PriorityOrdered interface for priority execution
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					// Get the Bean instance of BeanDefinitionRegistryPostProcessor type corresponding to BeanName from the container and add it to currentRegistryProcessors
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					// The BeanDefinitionRegistryPostProcessor corresponding to the record BeanName has been processed
					processedBeans.add(ppName);
				}
			}
			// Sort beans of BeanDefinitionRegistryPostProcessor type that implement the PriorityOrdered interface
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			// Add it to registryProcessors
			registryProcessors.addAll(currentRegistryProcessors);
			// Activate the postProcessBeanDefinitionRegistry method
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			// Empty the collection after execution
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			// Gets the BeanName of all beandefinitionregistrypostprocessors registered through configuration
			postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			// Traverse the beannames of all beandefinitionregistrypostprocessors registered through configuration
			for (String ppName : postProcessorNames) {
				// Filter out the unprocessed implementation classes that && implement the Ordered interface for the second execution
				if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
					// Get the Bean instance of BeanDefinitionRegistryPostProcessor type corresponding to BeanName from the container and add it to currentRegistryProcessors
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					// The BeanDefinitionRegistryPostProcessor corresponding to the record BeanName has been processed
					processedBeans.add(ppName);
				}
			}

			// Sort the beans of the BeanDefinitionRegistryPostProcessor type that implements the Ordered interface
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			// Add it to registryProcessors
			registryProcessors.addAll(currentRegistryProcessors);
			// Activate the postProcessBeanDefinitionRegistry method
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			// Empty the collection after execution
			currentRegistryProcessors.clear();

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			// Finally, obtain the Bean of BeanDefinitionRegistryPostProcessor type that does not implement the sorting interface for activation,
			// The while loop will not exit until all subclasses registered by BeanDefinitionRegistryPostProcessors are processed
			boolean reiterate = true;
			while (reiterate) {
				reiterate = false;
				// Gets the BeanName of all beandefinitionregistrypostprocessors registered through configuration
				postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
				// Traverse the beannames of all beandefinitionregistrypostprocessors registered through configuration
				for (String ppName : postProcessorNames) {
					// The remaining (subclasses that do not implement PriorityOrdered and Ordered sorting interfaces) unprocessed beannames
					if (!processedBeans.contains(ppName)) {
						// Get the Bean instance of BeanDefinitionRegistryPostProcessor type corresponding to BeanName from the container and add it to currentRegistryProcessors
						currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
						// The BeanDefinitionRegistryPostProcessor corresponding to the record BeanName has been processed
						processedBeans.add(ppName);
						reiterate = true;
					}
				}
				// sort
				sortPostProcessors(currentRegistryProcessors, beanFactory);
				// Add it to registryProcessors
				registryProcessors.addAll(currentRegistryProcessors);
				// Activate the postProcessBeanDefinitionRegistry method
				invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
				// Empty the collection after execution
				currentRegistryProcessors.clear();
			}

			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			// So far, the postProcessBeanDefinitionRegistry methods of all BeanDefinitionRegistryPostProcessor type objects have been activated,
			// Start activating its postProcessBeanFactory method.
			// registryProcessors records all beandefinitionregistrypostprocesses registered through hard coding
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
			// regularPostProcessors records all beanfactoryprocessors registered through hard coding
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}

		// If beanFactory instanceOf BeanDefinitionRegistry is false
		// Then directly execute the postProcessBeanFactory method of beanfactory postprocessor.
		// Because the parameter type required by the postProcessBeanDefinitionRegistry method defined in the BeanDefinitionRegistryPostProcessor interface is BeanFactory of BeanDefinitionRegistry type
		else {
			// Invoke factory processors registered with the context instance.
			// Directly activate the postProcessBeanFactory method of beanfactory postprocessor registered with hard coding
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}
		// So far, all beanfactoryprocessors registered through hard coding have been processed. Now start processing the post processor registered through configuration

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		// Get the BeanName of all post processors of beanfactoryprocessor type for subsequent processing
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		// Create several collections that save different sorts and call them in turn according to the implemented sorting interface
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		// Traverse the BeanName of all post processors of beanfactoryprocessor type obtained
		for (String ppName : postProcessorNames) {
			// If it has been processed above, skip it directly
			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.
		// sort
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		// activation
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

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

		// Finally, invoke all other BeanFactoryPostProcessors.
		// Activate beanfactoryprocessor that does not implement any sort interface
		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
		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();
	}

The specific implementation of the source code is very long, but it can be summarized as the specific implementation of the two points I mentioned above.

So far, all beanfactoryprocessors have been registered in the container and activated.

GitHub source address: https://github.com/kapbc/kapcb-spring-source/tree/master/Spring-Framework-v5.3.13

Note: This article is the author's notes on learning the Spring source code. In view of my limited technology, some mistakes are inevitable in the article. Thank you for your criticism and correction.

Topics: Java Spring Container