Spring Series Part 29: BeanFactory Extension (BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor)

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

  1. What does BeanFactoryPostProcessor do?

  2. What does BeanDefinitionRegistryPostProcessor do?

  3. What is the difference between BeanFactoryPostProcessor and BeanDefinitionRegistryPostProcessor?

  4. 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

  1. Implement the org.springframework.core.PriorityOrdered interface

  2. 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

  1. Implement the org.springframework.core.PriorityOrdered interface

  2. 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

  1. Pay attention to the four stages of spring: bean definition stage, BeanFactory post-processing stage, BeanPostProcessor registration stage, singleton bean creation and assembly stage

  2. BeanDefinitionRegistryPostProcessor will be called in the first stage to implement the bean registration operation. This stage will complete the registration of all beans

  3. 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.

  4. In phase 2, there are two prohibited operations: prohibiting the registration of beans and prohibiting obtaining beans from the container

  5. The implementation classes of the two interfaces introduced in this article can specify the order throughPriorityOrdered interface or Ordered 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