Chapter 4 source code extension point of Spring - beanfactoryprocessors

Posted by jmaker on Wed, 05 Jan 2022 22:11:44 +0100

I General description
II Analysis of Spring source code
III Examples

Premise: Spring version is 5.0 x

I General description

1. BeanFactoryPostProcessors
It is an important extension point interface provided during the creation of the Spring container. It has a postProcessBeanFactory() method. When the scanning is completed according to the specified scanning path, all ordinary classes under the path are transformed into BeanDefinition. This method can modify or overwrite the attribute information in a BeanDefinition.

@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;
}

2.BeanDefinitionRegistryPostProcessor
It is the core subclass interface of beanfactoryprocessors. It provides a method postProcessBeanDefinitionRegistry(), which can convert ordinary classes into BeanDefinition and then register them in the container

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;
}

II Analysis of Spring source code

When Spring starts, the configuration class AppConfig has been parsed and the corresponding BeanDefinition has been generated and registered in the container. BeanFactory and context environment have been initially completed. Next, you need to start scanning according to the scanning path defined by the annotation @ ComponentScan on the configuration class

AnnotationConfigApplicationContext.invokeBeanFactoryPostProcessors()

/**
	 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,  Instantiate and call all registered beanfactoryprocessors
	 * respecting explicit order if given.  If given, the explicit order is followed.
	 * <p>Must be called before singleton instantiation.  Must be invoked before a single instance is instantiated.
	 */
	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());  //★ core code

		// 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()));
		}
	}

Core source code: postprocessor registrationdelegate invokeBeanFactoryPostProcessors()

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
		// Invoke BeanDefinitionRegistryPostProcessors first, if any.

		// processedBeans is used to record the name of the implemented BeanDefinitionRegistryPostProcessor
		Set<String> processedBeans = new HashSet<>();

		//beanFactory is usually defaultlistablebeanfactory, and the if condition is true in most cases
		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

			//Class for manually added classes that directly implement the beanfactoryprocessor interface
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
			//Used to record classes that directly implement the BeanDefinitionRegistryPostProcessor interface
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

			// The first time: execute the postProcessBeanDefinitionRegistry() method of the manually added BeanDefinitionRegistryPostProcessor
			// The beanfactoryprocessors collection is generally empty unless we manually call the addbeanfactoryprocessor method of the container to add
			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.

			// It is used to record which classes implement BeanDefinitionRegistryPostProcessor. It can be used to manually register BeanDefinition into BeanDefinitionRegistry
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// Execute the scanned BeanDefinitionRegistryPostProcessor

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			// The second time: call the postProcessBeanDefinitionRegistry() method of BeanDefinitionRegistryPostProcessor that implements PriorityOrdered
			// There is only one ConfigurationClassPostProcessor class in postProcessorNames, which implements the BeanDefinitionRegistryPostProcessor and PriorityOrdered interfaces
			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));  // The specific implementation is the doGetBean method of AbstractBeanFactory
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory); // Ascending sort
			registryProcessors.addAll(currentRegistryProcessors);
			//★ the core is the doScan() method in the ClassPathBeanDefinitionScanner class, which scans according to the scan path defined by the @ ComponentScan annotation and registers it in the spring container
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			// The third time: scan out the postProcessBeanDefinitionRegistry() method of all beandefinitionregistrypostprocessors that implement Ordered
			// This includes custom classes that implement the BeanDefinitionRegistryPostProcessor interface
			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();

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.

			// The fourth time: execute the postProcessBeanDefinitionRegistry() method of ordinary BeanDefinitionRegistryPostProcessor that does not implement the PriorityOrdered or Ordered interface
			// During this process, another BeanDefinitionRegistryPostProcessor may be registered in BeanFactory, so it needs to be while until it is determined that all beandefinitionregistrypostprocessors have been executed
			// The prioritized or Ordered interfaces implemented by the BeanDefinitionRegistryPostProcessor registered in this process may not be executed in order
			// For example, if A registers B and C, and B registers D and E, then B and C will execute in order, and D and e will execute in order, but B, C, D and e cannot be guaranteed to execute in order as A whole
			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.

            // The fifth time: execute the postProcessBeanFactory() method of all scanned BeanDefinitionRegistryPostProcesso
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

			// The sixth time: execute the postProcessBeanFactory() method of the manually added ordinary beanfactory postprocessor
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		} else {
			// Invoke factory processors registered with the context instance.
			// When beanFactory is not an instance of BeanDefinitionRegistry, directly execute the manually added BeanDefinitionRegistryPostProcesso
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}

		// Execute the scanned ordinary beanfactoryprocessor

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		// Scan for all classes that directly implement the beanfactoryprocessor interface
		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.
		// The seventh time: execute the beanfactorypost processor that directly implements the beanfactorypost processor interface and implements the PriorityOrdered interface
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		// The eighth time: directly implement the beanfactoryprocessor interface and implement the beanfactoryprocessor of the Ordered interface
		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.
		// The ninth time: execute the ordinary beanfactoryprocessor that directly implements the beanfactoryprocessor interface
		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();
	}

From the source code, we can see that BeanDefinitionRegistryPostProcessor and beanfactoryprocessor

1. It can be divided according to the way of access to the process
(1) Add manually: call the addbeanfactoryprocessor method of the ApplicationContext container to add (specifically, implement the addbeanfactoryprocessor method in AbstractApplicationContext)
(2) Scanned by spring itself (including customized)

2. It can be divided by type
(1) Common type: if neither PriorityOrdered interface nor Ordered interface is implemented, the execution order is relatively backward
(2) Non ordinary type: it implements one or both of PriorityOrdered and Ordered

3. Execution order in the source code
(1) Execute the postProcessBeanDefinitionRegistry() method of the manually added BeanDefinitionRegistryPostProcessor
(2) Call the postProcessBeanDefinitionRegistry() method of BeanDefinitionRegistryPostProcessor that implements PriorityOrdered
At this time, there is only one BeanDefinitionRegistryPostProcessor in BeanFactory, that is, ConfigurationClassPostProcessor. This class implements the PriorityOrdered interface, so it takes priority in this step. Resolve the @ ComponentScan annotation of the configuration class to obtain the scanning path, and the scanning process is completed in this step.
(3) Execute the postProcessBeanDefinitionRegistry() method of all beandefinitionregistrypostprocessors that implement Ordered
(4) Execute the postProcessBeanDefinitionRegistry() method of an ordinary BeanDefinitionRegistryPostProcessor that does not implement the PriorityOrdered or Ordered interface
(5) Execute the postProcessBeanFactory() method of all scanned BeanDefinitionRegistryPostProcesso
(6) Execute the postProcessBeanFactory() method of the manually added normal beanfactoryprocessor
(7) The implementation directly implements the beanfactorypost processor interface and implements the beanfactorypost processor of the PriorityOrdered interface
(8) The implementation directly implements the beanfactorypost processor interface and implements the beanfactorypost processor of the Ordered interface
(9) Execute a normal beanfactoryprocessor that directly implements the beanfactoryprocessor interface

III Examples

##  Configuration class AppConfig
@ComponentScan({"com.maker","com.processor"})     // Defined scan path
@Configuration
@Description("Scan configuration class")
public class AppConfig {
}

##  UserService class - > under the path "com.maker"
@Component
@Lazy
@Scope
public class UserService{
    public void run() {
        System.out.println("People are running");
    }
}

## Three classes that are not under the scan path, makerservice, makerservice2, and makerservice3
public class MakerService {
    public void test() {
        System.out.println("MakerService -> test");
    }
}

public class MakerService2 {
    public void test() {
         System.out.println("MakerService2  -> test");
    }
}

public class MakerService3 {
    public void test() {
         System.out.println("MakerService3  -> test");
    }
}

## The custom BeanDefinitionRegistryPostProcessor directly implements the BeanDefinitionRegistryPostProcessor interface and indirectly implements beanfactoryprocessor. Therefore, it is necessary to rewrite the two methods (both under the defined scanning path)
### MakerBeanDefinitionRegistryPostProcessor is an ordinary BeanDefinitionRegistryPostProcessor
### MakerBeanDefinitionRegistryPostProcessor2 implements the PriorityOrdered interface
### MakerBeanDefinitionRegistryPostProcessor3 implements the Ordered interface

@Component
public class MakerBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MakerService.class);
        beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION);
        beanDefinition.setTargetType(MakerService.class);
        registry.registerBeanDefinition("makerService", beanDefinition);
        System.out.println("MakerServiceRegistryPostProcessor -> [ordinary] postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService = beanFactory.getBeanDefinition("makerService");
        makerService.setScope("prototype");
        System.out.println("MakerServiceRegistryPostProcessor -> [ordinary] postProcessBeanFactory, set up makerService As prototype bean");
    }
}
 
@Component
public class MakerBeanDefinitionRegistryPostProcessor2 implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MakerService2.class);
        beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION);
        beanDefinition.setTargetType(MakerService.class);
        registry.registerBeanDefinition("makerService2", beanDefinition);
        System.out.println("MakerServiceRegistryPostProcessor2 -> [PriorityOrdered] postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService2 = beanFactory.getBeanDefinition("makerService2");
        makerService2.setScope("prototype");
        System.out.println("MakerServiceRegistryPostProcessor2 -> [PriorityOrdered] postProcessBeanFactory, set up MakerService2 As prototype bean");
    }

    @Override
    public int getOrder() {
        System.out.println("MakerServiceRegistryPostProcessor2 -> [PriorityOrdered] getOrder");
        return 0;
    }
}

@Component
public class MakerBeanDefinitionRegistryPostProcessor3 implements BeanDefinitionRegistryPostProcessor, Ordered {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(MakerService3.class);
        beanDefinition.setRole(BeanDefinition.ROLE_APPLICATION);
        beanDefinition.setTargetType(MakerService.class);
        registry.registerBeanDefinition("makerService3", beanDefinition);
        System.out.println("MakerBeanDefinitionRegistryPostProcessor3 ->  [Ordered] postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService3 = beanFactory.getBeanDefinition("makerService3");
        makerService3.setScope("prototype");
        System.out.println("MakerServiceRegistryPostProcessor3 ->  [Ordered] postProcessBeanFactory, set up MakerService3 As prototype bean");
    }

    @Override
    public int getOrder() {
        System.out.println("MakerBeanDefinitionRegistryPostProcessor3 -> [Ordered] getOrder");
        return 0;
    }
}

## Three custom beanfactoryprocessors directly implement the beanfactoryprocessor interface, and only one method needs to be rewritten (all under the defined scanning path)
### MakerBeanFactoryPostProcessor is an ordinary beanfactoryprocessor
### MakerBeanFactoryPostProcessor2 implements the PriorityOrdered interface
### MakerBeanFactoryPostProcessor3 implements the Ordered interface

@Component
public class MakerBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService = beanFactory.getBeanDefinition("makerService");
        makerService.setScope("singleton");
        System.out.println("MakerBeanFactoryPostProcessor -> [ordinary] postProcessBeanFactory, modify MakerService Single example bean");
    }
}

@Component
public class MakerBeanFactoryPostProcessor2 implements BeanFactoryPostProcessor, PriorityOrdered {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService2 = beanFactory.getBeanDefinition("makerService2");
        makerService2.setScope("singleton");
        System.out.println("MakerBeanFactoryPostProcessor2 -> [PriorityOrdered] postProcessBeanFactory, modify MakerService2 Single example bean");
    }

    @Override
    public int getOrder() {
        System.out.println("MakerBeanFactoryPostProcessor2 -> [PriorityOrdered] getOrder");
        return 0;
    }
}

@Component
public class MakerBeanFactoryPostProcessor3 implements BeanFactoryPostProcessor, Ordered {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition makerService3 = beanFactory.getBeanDefinition("makerService3");
        makerService3.setScope("singleton");
        System.out.println("MakerBeanFactoryPostProcessor3 -> [Ordered] postProcessBeanFactory, modify MakerService3 Single example bean");
    }

    @Override
    public int getOrder() {
        System.out.println("MakerBeanFactoryPostProcessor3 -> [Ordered] getOrder");
        return 0;
    }
}
### Test execution sequence and function
public class SpringTest {
    public static void main(String[] args) {
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.run();

        // Gets whether the Scope of the bean is singleton or prototype. If it is not specified with the @ Scope annotation, it is singleton by default
        BeanDefinition beanDefinition = context.getBeanDefinition("makerService");
        System.out.println("MakerService Scope of: " + beanDefinition.getScope());
        MakerService makerService = context.getBean("makerService", MakerService.class);
        makerService.test();

        BeanDefinition beanDefinition2 = context.getBeanDefinition("makerService2");
        System.out.println("MakerService2 Scope of: " + beanDefinition2.getScope());
        MakerService2 makerService2 = context.getBean("makerService2", MakerService2.class);
        makerService2.test();

        BeanDefinition beanDefinition3 = context.getBeanDefinition("makerService3");
        System.out.println("MakerService3 Scope of: " + beanDefinition3.getScope());
        MakerService3 makerService3 = context.getBean("makerService3", MakerService3.class);
        makerService3.test();
    }
}

The operation results are as follows: verify the function and execution sequence

MakerServiceRegistryPostProcessor2 -> [PriorityOrdered] postProcessBeanDefinitionRegistry
MakerBeanDefinitionRegistryPostProcessor3 -> [Ordered] postProcessBeanDefinitionRegistry
Makerserviceregistrypostprocessor - > [normal] postProcessBeanDefinitionRegistry
Makerserviceregistrypostprocessor2 - > [priorityordered] postprocessbeanfactory, set MakerService2 as the prototype bean
Makerserviceregistrypostprocessor3 - > [ordered] postprocessbeanfactory, set MakerService3 as the prototype bean
Makerserviceregistrypostprocessor - > [normal] postProcessBeanFactory, set makerService as the prototype bean
Makerbeanfactorypostprocessor2 - > [priorityordered] postprocessbeanfactory, modify MakerService2 as a singleton bean
Makerbeanfactorypostprocessor3 - > [ordered] postprocessbeanfactory, modify MakerService3 as a singleton bean
Makerbeanfactorypostprocessor - > [normal] postProcessBeanFactory, modify MakerService as a singleton bean
People are running
Scope of MakerService: singleton
Makerservice - > test
Scope of MakerService2: singleton
Makerservice2 - > test
Scope of MakerService3: singleton
Makerservice3 - > test

Topics: Java Spring Back-end