This is the fourth day of my participation in the First Challenge 2022

BeanFactoryPostProcessor is an important extension point in Spring. It looks similar to BeanPostProcessor but has a slightly different trigger point. The BeanFactoryPostProcessor is in effect before all beans are instantiated. So what it does is load the BeanDefinition. Or you can change it dynamically or add a new BeanDefinition.

The class diagram

BeanFactoryPostProcessor has an extension interface that takes precedence over BeanFactoryPostProcessor’s methods.

The source code

Again, start with Refresh, the core method of Spring. The invokeBeanFactoryPostProcessors (the beanFactory); It’s for BeanFactoryPostProcessor.

@Override
public void refresh(a) throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
    // Prepare this context for refreshing.
    // Prepare to flush the container operation (set some status bits, listeners, events)
    prepareRefresh();

    // Tell the subclass to refresh the internal bean factory.
    / / instantiate a Bean plant (DefaultListableBeanFactory)
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // Prepare the bean factory for use in this context.
    // Initialize BeanFactory to initialize some attributes
    prepareBeanFactory(beanFactory);

    try {
        // Allows post-processing of the bean factory in context subclasses.
        // Empty method extension point (used in springboot)
        postProcessBeanFactory(beanFactory);

        // Invoke factory processors registered as beans in the context.
        // Call BeanFactoryPostProcessor --> (execute here!!)
        invokeBeanFactoryPostProcessors(beanFactory);

        // Register bean processors that intercept bean creation.
        // Register all beanPostProcessors
        registerBeanPostProcessors(beanFactory);

        // Initialize message source for this context.
        / / the internationalization
        initMessageSource();

        // Initialize event multicaster for this context.
        // Initializes the event broadcaster
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        // Extension point, implemented by subclasses
        onRefresh();

        // Check for listener beans and register them.
        // Register event listeners
        registerListeners();

        // Instantiate all remaining (non-lazy-init) singletons.
        // instantiate all non-lazy-loaded singletons
        finishBeanFactoryInitialization(beanFactory);

        // Last step: publish corresponding event.
        finishRefresh();
    }
    catch(BeansException ex) { ... }}Copy the code

Core processing method in PostProcessorRegistrationDelegate# invokeBeanFactoryPostProcessors, source has some comments. Here is a brief description of the process.

Step one: parameter passed in beanFactoryPostProcessors, by above knowable, there may be a BeanDefinitionRegistryPostProcessor may also be spring BeanFactoryPostProcessor implementation class. So we define two collections regularPostProcessors and registryProcessors to separate the two. Here is executed in the parameters passed in BeanDefinitionRegistryPostProcessor type of interface. (In SpringBoot, can be added in the boot method of the boot class)

Step 2: obtain all BeanDefinitionRegistryPostProcessor in the container, picked out the implementation PriorityOrdered interface classes, according to specific collation sort after one by one. (by default, this will only get ConfigurationClassPostProcessor, here is the function of processing configuration class, Add Configuration classes [that is, developer tags @Configuration, @Component, @importSelector, and so on] to the container.

Step 3: Get all BeanDefinitionRegistryPostProcessor in the container, selected contains the class implements Ordered interface, Filtering out BeanDefinitionRegistryPostProcessor (if an interface to realize Ordered and PriorityOrdered at the same time, it should be in the second step), sorted according to specific ordering rules one by one.

Step 4: get a container of all the BeanDefinitionRegistryPostProcessor in filtering has been carried out, the remaining did not implement all the classes, sorted according to specific ordering rules one by one. (here the cause of the infinite loop: dolls may happen, a method to realize the BeanDefinitionRegistryPostProcessor and created a new BeanDefinitionRegistryPostProcessor.)

This has done all the work in the containerBeanDefinitionRegistryPostProcessorExtension point of. And then there’s implementationBeanFactoryPostPostProcessorExtension points.

Step 5: to perform all of the above steps BeanDefinitionRegistryPostProcessor spring BeanFactoryPostProcessor implementation in the interface. (That is, the extension method in the extension interface has been implemented, in this place to implement the parent interface implementation method.)

Step 6: execution parameter passed beanFactoryPostProcessors only realize the spring BeanFactoryPostProcessor implementation in the interface.

Step 7: (BeanFactoryPostPostProcessor execution is simpler, summarize) in one step, obtain all realized spring BeanFactoryPostProcessor class in the container, Classify them into three categories: PriorityOrdered, Ordered, and those that don’t implement either interface. Then sort them according to the sorting rules and execute them one by one.

  1. Perform BeanDefinitionRegistryPostProcessor extension point

    1.1 perform parameter beanFactoryPostProcessors is BeanDefinitionRegistryPostProcessor implementation class extension point

    1.2 to perform in the spring container BeanDefinitionRegistryPostProcessor implementation and ordinal @ PriorityOrdered, @ Ordered

    1.3 execution container still exist BeanDefinitionRegistryPostProcessor implementation but did not join executed set the implementation class (registryProcessors)

  2. Perform BeanDefinitionPostProcessor extension point

    2.1 to perform the direct implementation BeanDefinitionRegistryPostProcessor collection (registryProcessors) spring BeanFactoryPostProcessor extension point

    2.2 Implement BeanFactoryPostProcessor extension points that directly implement BeanFactoryPostProcessor (regularPostProcessors). (If there are elements in this set, they are brought in by the developer by calling parameters.) In the first step the implementation is separated)

    2.3 Execute the BeanFactoryPostProcessor that has not been executed in the Spring container, in order of @Priorityordered, @Ordered, and finally, the BeanFactoryPostProcessor implementation classes that are not marked with the former two annotations

/** * Executes the BeanFactoryPostProcessor extension point **@Author: xiaocainiaoya
 * @Date: 2021/06/09 10:05:09
 * @param beanFactory
 * @paramBeanFactoryPostProcessors this is custom beanFactoryPostProcessors, default is empty, unless their extension *@return: * * /
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

	// Invoke BeanDefinitionRegistryPostProcessors first, if any.
	/ / processed BeanDefinitionRegistryPostProcessors type of bean name collection
	Set<String> processedBeans = new HashSet<>();

	if (beanFactory instanceof BeanDefinitionRegistry) {
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
		// A collection of generic PostProcessors (implementation classes that directly implement BeanFactoryPostProcessor)
		List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
		// registryProcessor(an implementation class that does not directly implement BeanFactoryPostProcessor) and has already called a collection of extension points
		List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

		/ / spring BeanFactoryPostProcessor BeanDefinitionRegistryPostProcessor parent interface, Perform the BeanDefinitionRegistryPostProcessor beanFactoryPostProcessors
		/ / and will be executed after BeanDefinitionRegistryPostProcessor placed in registryProcessors, Add an unexecuted BeanFactoryPostProcessor to regularPostProcessors
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
				/ / call BeanDefinitionRegistryPostProcessor extension point
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				registryProcessors.add(registryProcessor);
			}else {
				// Directly implements BeanFactoryPostProcessorregularPostProcessors.add(postProcessor); }}/ / put inside rear BeanDefinitionRegistryPostProcessor processor
		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

		/ / first, call the BeanDefinitionRegistryPostProcessors PriorityOrdered interface
		/ / (if no modification or secondary development, here will only return ConfigurationClassPostProcessor class
		The role of the / / ConfigurationClassPostProcessor class is to load the configuration class)
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		/ / execution BeanDefinitionRegistryPostProcessors extension point
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

		/ / second, call only implements the Ordered BeanDefinitionRegistryPostProcessors interface
		postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
		for (String ppName : postProcessorNames) {
			// Filter the called processedBean
			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();/ / in the end, call not tagged in processedBeans BeanDefinitionRegistryPostProcessors
		/ / the while loop is due to the possible dolls, such as A class implements the BeanDefinitionRegistryPostProcessor, in the call of A
    / / postProcessBeanDefinitionRegistry added a BeanDefinitionRegistryPostProcessor class B.
		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();
		}

		// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
		invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
	} else {
		// Invoke factory processors registered with the context instance.
		invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
	}

	// Don't initialize FactoryBeans here: we need to keep all uninitialized regular beans and let the bean factory post-processor apply them!
	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<>(orderedPostProcessorNames.size());
	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<>(nonOrderedPostProcessorNames.size());
	for (String postProcessorName : nonOrderedPostProcessorNames) {
		nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
	}
	invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
}
Copy the code

example

Spring’s lifecycle shows that BeanFactoryPostProcessor is executed before BeanPostProcessor is registered, or even before most beans are injected. However, if BeanFactoryPostProcessor is extensively used for extension, the order of execution should be considered if there are overlapping areas. For example, does the extension need to control whether the new BeanFactoryPostProcessor comes before or after the old BeanFactoryPostProcessor? This Order of execution is a key point that can be easily overlooked (the custom BeanFactoryPostProcessor can control the Order of execution by implementing the Order interface)

Example 1

Can be modified by BeanDefinitionRegistryPostProcessor BeanDefinition, such as there are so many places in the business scenario using the name injection way, now let the name into classes by a new class of container, replaces the old class.

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
      /** * replace class */
      // Get the old class in the scan path
      BeanDefinition beanDefinition = registry.getBeanDefinition("roomService");
      // Point it to a new class
      beanDefinition.setBeanClassName("cn.com.xiaocainiaoya.service.NewRoomService");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}Copy the code

Example 2

Comparatively core class ConfigurationClassPostProcessor springframwork scanning, the effect of this class is scan configuration class, Scans various Configuration annotations @Configuration, Component, ComponentScan, and so on in the specified path. These classes are parsed and added to the BeanDefinition.

The main purpose of the following methods is to derive further bean definitions from the configuration class in the registry, simply adding the configuration class to the Spring container according to the beanDefinition.

The execution sequence is:

  1. Complete the scan

  2. The definition of the configuration class completes the marking of the configuration class

  3. Handling of import

    3.1 ImportSelector(typically used for automatic assembly in SpringBoot)

    3.2 ImportBeanDefinitionRegistrar (typical usage in mybatis)

    3.3 Normal Class: No special annotation or @import added

    3.4 ImportResource

  4. @ Bean processing

  5. @bean in the interface

  6. @ PropertySource processing

  7. Handling of inner classes

  8. Parent class processing

/** * Derive further bean definitions from the configuration classes in the registry. */
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	// Generate an ID, place it and repeat
	int registryId = System.identityHashCode(registry);

	// Throw an exception if executed repeatedly
	if (this.registriesPostProcessed.contains(registryId)) {
		throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
	}
	if (this.factoriesPostProcessed.contains(registryId)) {
		throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);
	}

	// Indicates that the BeanDefinition collection action in this Registry has been done to avoid collecting the registry again
	this.registriesPostProcessed.add(registryId);

	// According to the configuration class, collect all bd information and make mark: Full mode or Lite mode
	processConfigBeanDefinitions(registry);
}
Copy the code

Matters needing attention

BeanFactoryPostProcessor spring allows you to handle BeanDefinitions, but you must comply with the Spring specification and not instantiate the bean. That is, bean instantiation cannot be triggered in BeanFactoryPostProcessor. Such as:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      // The getBeansOfType call triggers the getBean method, which causes the Bean to be injected prematurely at this node in the lifecycle
      // The auto-injected BeanPostProcessor has not been injected yet and will not be used during bean creation, so it will cause
      // RoomService property injection will fail.Map<String, RoomService> map = beanFactory.getBeansOfType(RoomService.class); }}Copy the code

So, inBeanFactoryPostProcessorBe sure not to trigger when usingBeanAdvance instantiation of.