Springboot extension point BeanPostProcessor

Collection of implementation methods and working principles of Springboot extension point series:

Springboot extension point ApplicationContextInitializer

Springboot extension point BeanFactoryPostProcessor

Springboot extension point BeanDefinitionRegistryPostProcessor

Springboot extension point BeanPostProcessor

Springboot extension point InstantiationAwareBeanPostProcessor

Springboot extension point SmartInstantiationAwareBeanPostProcessor

Springboot extension point ApplicationContextAwareProcessor

Springboot extension point @PostConstruct

Springboot extension point InitializingBean

Springboot extension point SmartInitializingSingleton

Springboot extension point CommandLineRunner and ApplicationRunner

Springboot extension point FactoryBean

Springboot extension point DisposableBean

The end of the Springboot extension point series: Bean life cycle

Preface

There are actually many extension points of Springboot (Spring), but they all have one thing in common. They all revolve around Bean and BeanFactory (container). In fact, this is also easy to understand. The core of Spring is control inversion, dependency injection, and orientation. Aspect programming, and then put aside all the details, what did you find? Spring provides a container to manage Beans, and the entire ecosystem seems to revolve around this. Studying the meaning of source code lies on the one hand in the technology itself and on the other hand in understanding and accepting the ideas in it.

You will always get lost if you wander without a purpose. It is different if you have a goal. Therefore, this article is centered around the following questions. This is what I want to share with you: (If you have the same question as me, Follow, collect + like, don’t get lost)

  • 1. What are the functional characteristics of the BeanPostProcessor interface?

  • 2. How to extend the BeanPostProcessor interface?

  • 3. What is the working principle of the implementation class of BeanPostProcessor interface?

  • 4. What are the application scenarios of the BeanPostProcessor interface?

Features

  • 1. BeanPostProcessor is a Bean-level extension interface. After the Bean instantiation managed by Spring is completed, two extension points are reserved;

  • 2. The implementation method of these two extensions is to implement the BeanPostProcessor interface and register the implementation class in the Spring container;

  • 3. The two extension points are the postProcessBeforeInitialization method and the postProcessAfterInitialization method of the BeanPostProcessor interface;

  • 4. The execution timing of the postProcessBeforeInitialization method is after the Spring-managed Bean instantiation and property injection are completed, and before the InitializingBean#afterPropertiesSet method and the custom initialization method;

  • 5. The execution timing of the postProcessAfterInitialization method is after the InitializingBean#afterPropertiesSet method and the custom initialization method;

  • 6. The postProcessBeforeInitialization method and postProcessAfterInitialization method of the implementation class of the BeanPostProcessor interface will be executed after each bean managed by Spring is initialized;

Method to realize

1. Define an entity class Dog, implement the InitializingBean interface, and implement afterPropertiesSet(). Among them, afterPropertiesSet() and init() are to demonstrate the execution timing of the postProcessBeforeInitialization method and postProcessAfterInitialization method of the implementation class of the BeanPostProcessor interface;

@Getter
@Setter
@Slf4j
public class Dog implements InitializingBean {
    
    
    private String name = "旺财";
    private String color = "黑色";
    public Dog() {
    
    
        log.info("---dog的无参构造方法被执行");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        log.info("---afterPropertiesSet被执行");
    }
    public void init() {
    
    
        log.info("---initMethod被执行");
    }
}

Register the Dog class into the Spring container and set the initialization method after Bean instantiation;

@Configuration
public class SpringConfig {
    
    
    @Bean(initMethod = "init")
    public Dog dog(){
    
    
        Dog dog = new Dog();
        return dog;
    }
}

2. Define MyBeanPostProcessor and implement the BeanPostProcessor interface; (The naming of the class and the logic within the method are only for demonstration purposes. In actual development, the demonstration content needs to be replaced with actual logic)

@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        if (beanName.equals("dog")) {
    
    
            log.info("postProcessBeforeInitialization---" + beanName);
            //如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
        }
        return bean;
    }
 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        if (beanName.equals("dog")) {
    
    
            log.info("postProcessAfterInitialization---" + beanName);
            //如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
        }
        return bean;
    }
}

3. Write unit tests to verify the results;

@SpringBootTest
@Slf4j
public class FanfuApplicationTests {
    
    
   @Test
    public void test3(){
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
        Dog dog = ((Dog) context.getBean("dog"));
        log.info(dog.getName());
    }
}

Insert image description here

Conclusion: Judging from the execution results of the unit test, it is verified that the execution timing of Spring's extension point BeanPostProcessor, that is, the execution timing of the postProcessBeforeInitialization method is after the Spring-managed Bean instantiation and property injection are completed, the InitializingBean#afterPropertiesSet method and the customized Before the initialization method; the execution timing of the postProcessAfterInitialization method is before and after the InitializingBean#afterPropertiesSet method and the custom initialization method;

The above demonstrates the implementation method and execution timing of BeanPostProcessor as one of the extension points of Springboot. Let's start with an example to understand its basic working principle. As the saying goes, to know what is happening, you must also know why.

working principle

The key to the working principle of eanPostProcessor is actually two points. First, when was the implementation class of BeanPostProcessor registered? Second, how are the postProcessBeforeInitialization method and postProcessAfterInitialization method of the BeanPostProcessor implementation class executed?

Registration time

  • 1. Among the two extension methods in BeanPostProcessor, the postProcessBeforeInitialization method is executed first, that is, after the Bean instantiation and attribute injection are completed, the entrance of the implementation class of the BeanPostProcessor interface to the Spring container is found through debugging of the implementation sample code. , namely org.springframework.context.support.AbstractApplicationContext#refresh—>registerBeanPostProcessors
    Insert image description here

  • 2. Enter the AbstractApplicationContext#registerBeanPostProcessors method and you will find that this code is very clean, that is, it relies on the registerBeanPostProcessors() method of the PostProcessorRegistrationDelegate class;

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    
    
   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
  • 3. Entering the registerBeanPostProcessors() method of the PostProcessorRegistrationDelegate class is another story: the first step is to obtain the names of all implementation classes that implement the BeanPostProcessor interface. MyBeanPostProcessors in the implementation example is among them;
    Insert image description here

The second step is to register the BeanPostProcessorChecker in advance. Its main purpose is to print log information during the Bean creation process;
Insert image description here

The third step is to divide all the implementation classes of the BeanPostProcessor interface into three groups according to whether they implement the PriorityOrdered interface, whether they implement the Ordered interface, and others;
Insert image description here

Finally, the following content is very long, but very simple, that is, according to the three categories divided in the second step, register in order. The specific order is the implementation class of the BeanPostProcessor interface that implements the PriorityOrdered interface, the implementation class of the BeanPostProcessor interface that implements the Ordered interface, and others. Implementation class of BeanPostProcessor interface;
Insert image description here

In summary, the registration timing of BeanPostProcessor is during the startup process of the Spring container, that is, after the logic execution of the BeanFactoryPostProcessor extension point is completed, the registration of BeanPostProcessor begins. The specific registration logic is in PostProcessorRegistrationDelegate#registerBeanPostProcessors().

execution timing

It is verified from the example of the implementation method that the execution time of the implementation class of the BeanPostProcessor interface is after the instantiation and attribute injection of the Spring-managed Bean are completed. Then the instantiation entry of the Dog class is found, and then the implementation class of the BeanPostProcessor interface is executed. The time is not far away.

1. Through Debug debugging, register the instantiation entry of the Dog class in the Spring container, that is, org.springframework.context.support.AbstractApplicationContext#refresh—>finishBeanFactoryInitialization();
Insert image description here

2. Enter finishBeanFactoryInitialization() and find that the Dog class in the implementation example is instantiated in DefaultListableBeanFactory#preInstantiateSingletons—>getBean(). Here is a brief introduction to the business logic of getBean(): when obtaining a certain bean, first query the cache to determine whether it exists. If it exists, return it directly. If it does not exist, start creating the Bean. If the Bean depends on another Bean, then It is a recursion of the above process.
Insert image description here

3. After entering from the getBean method, the main process is AbstractBeanFactory#doGetBean–>AbstractBeanFactory#createBean–>AbstractAutowireCapableBeanFactory#doCreateBean–>AbstractAutowireCapableBeanFactory#createBeanInstance. At this point, the instantiation and attribute injection of the Bean are completed. At this point, cheer up. The execution time of the implementation class of the BeanPostProcessor interface you are looking for is about to arrive. Sure enough, in the AbstractAutowireCapableBeanFactory#doCreateBean method, after the Dog class is instantiated, initializeBean() is called to initialize the bean. The execution timing of the postProcessBeforeInitialization method and postProcessAfterInitialization method of the implementation class of the BeanPostProcessor interface is before and after the execution of the Bean's initialization method. Triggered, then this method is most likely the entrance to the execution timing of the implementation class of the BeanPostProcessor interface.
Insert image description here

4. Enter initializeBean() and the judgment is indeed correct. First execute the postProcessBeforeInitialization method of the BeanPostProcessor interface implementation class. Then if the bean implements InitializingBean or customizes initMethod, the InitializingBean#afterPropertiesSet and initMethod methods will be executed here. Finally, the postProcessAfterInitialization method of the BeanPostProcessor interface implementation class will be executed;

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    
    
   if (System.getSecurityManager() != null) {
    
    
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    
    
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }else {
    
    
      invokeAwareMethods(beanName, bean);
   }
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
    
    
       //执行BeanPostProcessor接口实现类的postProcessBeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
    
    
       //如果bean实现了InitializingBean或者自定义了initMethod,
       //会在这里执行InitializingBean#afterPropertiesSet和initMethod方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
    
    
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
    
    
       //执行BeanPostProcessor接口实现类的postProcessAfterInitialization方法
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

5. Next, enter applyBeanPostProcessorsBeforeInitialization(), invokeInitMethods(), applyBeanPostProcessorsAfterInitialization() respectively to see how they are implemented. Let’s first look at applyBeanPostProcessorsBeforeInitialization(): If you have carefully studied the previous articles of BeanFactoryPostProcessor of Springboot extension point, BeanDefinitionRegistryPostProcessor of Springboot extension point, and ApplicationContextInitializer of Springboot extension point, then you will be very familiar with the routine of this method: first obtain All the implementation classes registered to the BeanPostProcessor interface in the Spring container are then traversed and executed to trigger the method. It is so simple and unpretentious.

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
    
    
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
    
    
         return result;
      }
      result = current;
   }
   return result;
}

6. Let’s take a look at AbstractAutowireCapableBeanFactory#invokeInitMethods. The logic is also very clear. First determine whether the InitializingBean interface is implemented. If the InitializingBean interface is implemented, it will trigger the execution of afterPropertiesSet(), and then determine whether there is a custom initMethod method. If so, then Start execution here;

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
    
    
    //判断是否实现了InitializingBean接口
   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    
    
      if (logger.isTraceEnabled()) {
    
    
         logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
    
    
         try {
    
    
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
    
    
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
    
    
            throw pae.getException();
         }
      }else {
    
    
          //如果实现了InitializingBean接口,就会重写afterPropertiesSet(),这里就会触发执行
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
   if (mbd != null && bean.getClass() != NullBean.class) {
    
    
       //判断有没有自定义initMethod方法,如果有,则在这里开始执行;
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
    
    
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

7. Finally, let’s take a look at applyBeanPostProcessorsAfterInitialization(). If you understand applyBeanPostProcessorsBeforeInitialization() before, there is no need to analyze it here. It is exactly the same. You are familiar with the recipe and the taste.

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
    
    
 
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
    
    
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
    
    
         return result;
      }
      result = current;
   }
   return result;
}

At this point, the working principle of the Springboot extension point BeanPostProcessor has been analyzed. It boils down to two points. First, during the initialization process of the Spring container, the registration of the extension point is completed; second, after the Bean is instantiated and attribute injected in Spring, the Trigger extension actions for registered extension points. The content is very long, but the logic is simple. I hope that friends who read this article will have the patience to read it, because after I have studied the whole process clearly, I feel that I have benefited a lot, and I hope you will too.

Application scenarios

In fact, after understanding the functional features, implementation methods and working principles of BeanPostProcessor, you can apply this extension point when encountering similar business needs. Here are two application scenarios that come to mind:

  • Processing custom annotations
    In the program, we can customize annotations and mark them on the corresponding classes. When a class is registered in the Spring container and instantiated, and we want to trigger some other operations corresponding to the custom annotations, we can Implemented through BeanPostProcessor.

  • There are two previous articles on parameter verification
    : Elegant Springboot Parameter Verification (1) and Elegant Springboot Parameter Verification (2). They share with you the specific implementation method of parameter verification. The core principle is to use the BeanPostProcessor extension point. Specifically The implementation class is org.springframework.validation.beanvalidation.BeanValidationPostProcessor

——————————————
Copyright statement: This article is an original article by CSDN blogger “Ordinary Trafficker” and follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this copy when reprinting statement.
Original link: https://blog.csdn.net/fox9916/article/details/128917992

Guess you like

Origin blog.csdn.net/tian830937/article/details/132947732