There are two very important interfaces in Spring: BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor. These two interfaces are often asked in interviews. In this article, we will take down them both.
Let’s look at a few questions first
-
What does BeanFactoryPostProcessor do?
-
What does BeanDefinitionRegistryPostProcessor do?
-
What is the difference between BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor?
-
What is the execution order of these interfaces?
The four main stages in the Spring container
-
Phase 1: Bean registration phase, this phase will complete the registration of all beans
-
Phase 2: BeanFactory post-processing phase
-
Phase 3: Register BeanPostProcessor
-
Phase 4: Bean creation phase. This phase completes the registration and loading operations of all singleton beans. This phase is not the focus of this article. If you are interested, you can read the detailed introduction in the previous article: Bean life cycle detailed explanation
The two interfaces introduced in this article are mainly related to the first two stages. Below we mainly look at the first two stages.
Phase 1: Bean registration phase
Overview
The registration of all beans in spring will be completed at this stage. According to the specification, the registration of all beans must be carried out at this stage. Do not register beans at other stages.
At this stage, spring provides us with an interface: BeanDefinitionRegistryPostProcessor. At this stage, the spring container will obtain all beans of type BeanDefinitionRegistryPostProcessor
in the container, and then call their postProcessBeanDefinitionRegistry
method. , the source code is as follows, the method parameter type is BeanDefinitionRegistry
, this type is familiar to everyone, that is, the bean definition register, which provides some internal methods to register beans in the container.
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
This interface also inherits the BeanFactoryPostProcessor
interface. You don’t need to worry about this for now. It will be introduced in Phase 2 later.
When there are multiple BeanDefinitionRegistryPostProcessor
in the container, you can specify the order in any of the following ways
-
Implement the
org.springframework.core.PriorityOrdered
interface -
Implement the
org.springframework.core.Ordered
interface
Execution order:
PriorityOrdered.getOrder() asc,Ordered.getOrder() asc
Let’s use a case to feel the effect.
Case 1: Simple and practical
This case demonstrates the simple use of BeanDefinitionRegistryPostProcessor
Customize a BeanDefinitionRegistryPostProcessor
Below we define a class that needs to implement the BeanDefinitionRegistryPostProcessor
interface, and then we will implement 2 methods. Everyone will focus on the postProcessBeanDefinitionRegistry
method. The other method comes from BeanFactoryPostProcessor
, we will introduce this method later. In the postProcessBeanDefinitionRegistry
method, we define a bean and then register it to the container through registry
. The code is very simple
package com.javacode2018.lesson003.demo3.test0; @Component public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { //Define a bean of string type AbstractBeanDefinition userNameBdf = BeanDefinitionBuilder. genericBeanDefinition(String.class). addConstructorArgValue("passerby"). getBeanDefinition(); //Register userNameBdf into the spring container registry.registerBeanDefinition("userName", userNameBdf); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
A configuration class in the same package
package com.javacode2018.lesson003.demo3.test0; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig0 { }
Test cases
package com.javacode2018.lesson003.demo3; import com.javacode2018.lesson003.demo3.test0.MainConfig0; import com.javacode2018.lesson003.demo3.test1.MainConfig1; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class BeanDefinitionRegistryPostProcessorTest { @Test public void test0() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig0.class); context.refresh(); System.out.println(context.getBean("userName")); } }
Run output
Passers-by
Case 2: Multiple specified sequences
Below we define 2 BeanDefinitionRegistryPostProcessor
, both of which implement the Ordered
interface. The value of the first order is 2, and the value of the second order is 1. Let’s take a look at the specific execution. Order.
First
package com.javacode2018.lesson003.demo3.test1; @Component public class BeanDefinitionRegistryPostProcessor1 implements BeanDefinitionRegistryPostProcessor, Ordered { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println(String.format("BeanDefinitionRegistryPostProcessor1{order=%d},Register name bean,", this.getOrder())); //Define a bean AbstractBeanDefinition nameBdf = BeanDefinitionBuilder. genericBeanDefinition(String.class). addConstructorArgValue("passer-by java"). getBeanDefinition(); //Register the defined bean to the container registry.registerBeanDefinition("name", nameBdf); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public int getOrder() { return 2; } }
Second
package com.javacode2018.lesson003.demo3.test1; @Component public class BeanDefinitionRegistryPostProcessor2 implements BeanDefinitionRegistryPostProcessor, Ordered { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println(String.format("BeanDefinitionRegistryPostProcessor2{order=%d},Register car bean,", this.getOrder())); //Define a bean AbstractBeanDefinition nameBdf = BeanDefinitionBuilder. genericBeanDefinition(String.class). addConstructorArgValue("Porsche"). getBeanDefinition(); //Register the defined bean to the container registry.registerBeanDefinition("car", nameBdf); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } @Override public int getOrder() { return 1; } }
The first line of the postProcessBeanDefinitionRegistry method in the above two classes has output, and the order of execution can be seen through the running results.
Add configuration class in the same package
package com.javacode2018.lesson003.demo3.test1; import org.springframework.context.annotation.ComponentScan; @ComponentScan public class MainConfig1 { }
Test case
@Test public void test1() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig1.class); context.refresh(); context.getBeansOfType(String.class).forEach((beanName, bean) -> { System.out.println(String.format("%s->%s", beanName, bean)); }); }
Run output
BeanDefinitionRegistryPostProcessor2{order=1}, register car bean, BeanDefinitionRegistryPostProcessor1{order=2}, register name bean, car->Porsche name->Passer A java
Summary
BeanDefinitionRegistryPostProcessor
has a very important implementation class:
org.springframework.context.annotation.ConfigurationClassPostProcessor
Some people may not be familiar with this class. You should be familiar with the following annotations. These annotations are implemented in the above class. Through these annotations, batch registration of beans is realized.
@Configuration @ComponentScan @Import @ImportResource @PropertySource
Interested friends can take a look at ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
and study the parsing process of the above annotations, and you can learn a lot.
Phase 2: BeanFactory post-processing phase
Overview
At this stage, the spring container has completed the registration of all beans. At this stage, you can modify some information in the BeanFactory, such as modifying the definition information of some beans in stage 1, modifying some configurations of the BeanFactory, etc. This Spring also provides an interface for expansion: BeanFactoryPostProcessor
, referred to as bfpp
. There is a method in the interface postProcessBeanFactory
, and spring will obtain all the information in the container. beans of type BeanFactoryPostProcessor, and then call their postProcessBeanFactory
. Take a look at the source code of this interface:
@FunctionalInterface public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
When there are multiple BeanFactoryPostProcessor
in the container, you can specify the order in any of the following ways
-
Implement the
org.springframework.core.PriorityOrdered
interface -
Implement the
org.springframework.core.Ordered
interface
Execution order:
PriorityOrdered.getOrder() asc,Ordered.getOrder() asc
Let’s use a case to feel the effect.
Case
This case demonstrates that the BeanFactoryPostProcessor is used to modify the registered bean definition information in the bean and set a value to a bean attribute.
First define a bean
package com.javacode2018.lesson003.demo3.test2; import org.springframework.stereotype.Component; @Component public class LessonModel { //Course Title private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "LessonModel{" + "name='" + name + ''' + '}'; } }
The above bean has a name field and no value is set. Next, we set the value in BeanFactoryPostProcessor.
Customized BeanFactoryPostProcessor
In the following code, we first obtain the definition information of the lessonModel
bean, and then set a value to its name
attribute.
package com.javacode2018.lesson003.demo3.test2; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("Ready to modify lessonModel bean definition information!"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition("lessonModel"); beanDefinition.getPropertyValues().add("name", "spring master series!"); } }
Test cases
@Test public void test2() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig2.class); context.refresh(); System.out.println(context.getBean(LessonModel.class)); }
Run output
Prepare to modify the lessonModel bean definition information! LessonModel{name='spring master series!'}
It can be seen from the results that the bean definition information registered in the container is modified through BeanFactoryPostProcessor
.
Several important implementation classes of this interface
PropertySourcesPlaceholderConfigurer
Does anyone know what this interface does? Let’s look at a piece of code
<bean class="xxxxx"> <property name="userName" value="${userName}"/> <property name="address" value="${address}"/> </bean>
Everyone is familiar with this. Spring processes the ${xxx}
in the attributes in xml in PropertySourcesPlaceholderConfigurer#postProcessBeanFactory
, and parses this format into a real value.
CustomScopeConfigurer
Register a custom Scope object in the container, that is, register a custom scope implementation class. For those who don’t know about custom scopes, it is recommended to read it first: Spring Series Part 6: Play with bean scope and avoid jumping In the pit!
This usage is relatively simple. Just define a CustomScopeConfigurer
bean, and then register the custom bean through this class.
EventListenerMethodProcessor
Processing the @EventListener
annotation, that is, the event mechanism in spring, you need to understand spring events: Detailed explanation of spring event mechanism
There are also some implementation classes, which will not be introduced here.
Usage precautions
There is one thing that needs to be noted when using the BeanFactoryPostProcessor
interface. In its postProcessBeanFactory
method, it is strongly prohibited to obtain other beans through the container. This will cause the bean to be initialized in advance, which will Some unexpected problems occurred because BeanPostProcessor
was not ready at this stage. As introduced in the first four stages of this article, BeanPostProcessor
was registered in the third stage. Spring container, and BeanPostProcessor
can intervene in the bean creation process. For example, aop in spring is implemented in some subclasses of BeanPostProcessor
, @Autowired
is also processed in the subclass of BeanPostProcessor
. If you get the bean at this time, the bean will not be processed by BeanPostProcessor
, so the created bean may be If you have any questions, let me demonstrate it through a case to make it more transparent.
Let’s create a simple class
package com.javacode2018.lesson003.demo3.test3; import org.springframework.beans.factory.annotation.Autowired; public class UserModel { @Autowired private String name; //@1 @Override public String toString() { return "UserModel{" + "name='" + name + ''' + '}'; } }
@1: @Autowired is used, which will specify injection
Let’s create a configuration class
Two UserModel type beans are defined in the configuration class: user1, user2
And a String type bean: name is defined, which will be injected into the name attribute in UserModel.
package com.javacode2018.lesson003.demo3.test3; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class MainConfig3 { @Bean public UserModel user1() { return new UserModel(); } @Bean public UserModel user2() { return new UserModel(); } @Bean public String name() { return "Java is a passer-by, and will help you become a Java master!"; } }
Test cases
Output all UserModel type beans in the container
@Test public void test3() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(MainConfig3.class); context.refresh(); context.getBeansOfType(UserModel.class).forEach((beanName, bean) -> { System.out.println(String.format("%s->%s", beanName, bean)); }); }
Run output
user1->UserModel{name='Java, a passer-by, guide everyone to become a Java master!'} user2->UserModel{name='Java, the first passer-by, guide everyone to become a Java master!'}
The effect does not require much explanation, everyone can understand it at a glance. Let’s focus on it.
Add a BeanFactoryPostProcessor
Get the user1 bean in the postProcessBeanFactory
method
package com.javacode2018.lesson003.demo3.test3; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.getBean("user1"); } }
Run the output again
user1->UserModel{name='null'} user2->UserModel{name='Java, the first passer-by, guide everyone to become a Java master!'}
Note that the name in user1 has become null. What’s going on?
This is because the @Autowired annotation is parsed in AutowiredAnnotationBeanPostProcessor
, and the spring container calls the use of BeanFactoryPostProcessor#postProcessBeanFactory
. At this time, there is no AutowiredAnnotationBeanPostProcessor
in the spring container. , so when you get the user1 bean at this time, @Autowired will not be processed, so the name is null.
Source code
Source code of 4 stages
The source code of the 4 stages is located in the following method
org.springframework.context.support.AbstractApplicationContext#refresh
Part of the code intercepted in this method is as follows:
//Corresponding to stages 1 and 2: calling the factory processor registered as a bean in the context, that is, calling the methods in the two interfaces introduced in this article invokeBeanFactoryPostProcessors(beanFactory); // Corresponding to stage 3: Register the bean processor created by the interception bean, that is, register BeanPostProcessor registerBeanPostProcessors(beanFactory); //Corresponding to phase 3: Instantiate all remaining (non-lazy initialized) singletons. finishBeanFactoryInitialization(beanFactory);
The source code of Phase 1 and Phase 2 is located in the method below. The code is relatively simple. I strongly recommend that you take a look and you can understand it in a few minutes.
org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)
Summary
-
Pay attention to the four stages of spring: bean definition stage, BeanFactory post-processing stage, BeanPostProcessor registration stage, singleton bean creation and assembly stage
-
BeanDefinitionRegistryPostProcessor will be called in the first stage to implement the bean registration operation. This stage will complete the registration of all beans
-
BeanFactoryPostProcessor will be called in the second stage. By this stage, the bean has completed all bean registration operations. In this stage, you can modify some information in the BeanFactory, such as modifying some information in stage 1. Bean definition information, modify some configurations of BeanFactory, etc.
-
In phase 2, there are two prohibited operations: prohibiting the registration of beans and prohibiting obtaining beans from the container
-
The implementation classes of the two interfaces introduced in this article can specify the order through
PriorityOrdered
interface orOrdered
interface
Case source code
https://gitee.com/javacode2018/spring-series
All case codes of Passerby Java will be put on this page in the future. Please watch it and continue to pay attention to the news.
Homework
2 questions, welcome to leave a message to discuss, there will be a surprise red envelope!
Question 1
Please read the source code of org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors, describe the main internal execution process through text, and then leave a message
Question 2
What is the difference between BeanPostProcessor and BeanFactoryPostProcessors? message
The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 128,439 people are learning the system