🍃

A person’s greatest challenge, is how to overcome their own shortcomings.

🍃 Premise summary

Spring has great extensibility, but what is the extensibility of this extension? BeanPostProcessor is one of the best examples of Spring extensibility.

🍃 BeanPostProcessor’s role

  • Simply put, BeanPostProcessor provides a method for callbacks before and after initialization, and by extension we mean extending beans before and after instantiation.

  • BeanDefinition is registered after the BeanDefinition is registered, and the methods defined are called before and after instantiation during the Bean creation process;

The operation object is an instantiated and property-populated Bean instance to be initialized.

🍃 source code analysis


public interface BeanPostProcessor {
  /** * call */ before initialization
   @Nullable
   default Object postProcessBeforeInitialization(Object bean, String beanName) 
	   throws BeansException {
      return bean;
   }
   /** * call */ after initialization
   @Nullable
   default Object postProcessAfterInitialization(Object bean, String beanName) 
	   throws BeansException {
      returnbean; }}Copy the code
  • This is the definition of the BeanPostProcessor interface, and you can see from the method name that these two methods are called before initialization and after initialization.

  • Note that the method returns either the original instance or the wrapped instance. If null is returned, the subsequent BeanPostProcessor will not take effect (multiple BeanPostProcessors can be registered).

🍃 How to use

The code for BeanPostProcessorDemo is as follows:


public class BeanPostProcessorDemo {
    public static void main(String[] args) {
        // Create the base container
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // Load the XML configuration file
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("spring-bean-post-processor.xml");
        / / add BeanPostProcessor
        beanFactory.addBeanPostProcessor(newUserBeanPostProcessor()); User user = beanFactory.getBean(User.class); System.out.println(user); }}@Data
class User{
    private String userName;
    private Integer age;
    private String beforeMessage;
    private String afterMessage;
	
    public void initMethod(a){
        System.out.println("Initialize :"+this);
        this.setUserName("Xiao Ming");
        this.setAge(18); }}class UserBeanPostProcessor implements BeanPostProcessor{
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
		throws BeansException {
        if (bean instanceof User){
            System.out.println("Before initialization :"+bean);
            ((User) bean).setBeforeMessage("Pre-initialization information");
        }
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws 
		BeansException {
        if (bean instanceof User){
            System.out.println("After initialization :"+bean);
            ((User) bean).setAfterMessage("Post-initialization information");
        }
        returnbean; }}Copy the code

Others omit……

After running, the print result is as follows:

BeforeMessage = NULL, afterMessage= NULL. Initialization :User(userName= NULL, age= NULL, beforeMessage=null, afterMessage= NULL) BeforeMessage = information before initialization, afterMessage=null) After initialization :User(userName= xiaoming, age=18, beforeMessage= information before initialization, AfterMessage =null) User(userName= xiaoming, age=18, beforeMessage= information before initialization, afterMessage= information after initialization)Copy the code

The code above is very simple to create the base container, because I’m using the BeanFactory in this case, and the BeanFactory is the base container that I manually registered with the BeanPostProcessor. It can also be scanned or defined to a container.

The print results are analyzed below:

  1. Before initialization :User(userName= NULL, age= NULL, beforeMessage=null, afterMessage=null)

The content of the result is that the output of postProcessBeforeInitialization method, when the User instance is instantiated, not in the initialization step, so all the attribute is null, shows that the method is initialized. The init method of the bean object.

  1. Initialization :User(userName= NULL, age= NULL, beforeMessage= information before initialization, afterMessage=null)

The results for a custom initialization method initMethod method the contents of the output, this time the User instance initialization, real and we in postProcessBeforeInitialization beforeMessage in the values in the set.

  1. After initialization :User(userName= xiaoming, age=18, beforeMessage= information before initialization, afterMessage=null)

That the result is that the output of postProcessAfterInitialization content, can be seen from the result it is, indeed, after the custom initMethod.

🍃 Spring lifecycle

Beans in Spring are generally divided into four cycles: instantiation, attribute assignment, initialization, and destruction. BeanPostProcessor executes before and after the initialization phase.

  • First of all to seeAbstractAutowireCapableBeanFactoryIn thedoCreateBeanMethod, the method is actuallyCreate the specified BeanMethods.
    • Three important method calls are createBeanInstance, populateBean, and initializeBean.
    • These three methods represent the instantiation, attribute assignment, and initialization life cycles in Spring beans, respectively.

BeanPostProcessor is called before and after initialization, so let’s just look at the method details in the initializeBean. Details of the method are as follows:

protected Object initializeBean(String beanName, Object bean, @Nullable 
								RootBeanDefinition mbd) {
   // Handle BeanNameAware, BeanClassLoaderAware, BeanFactoryAware
   if(System.getSecurityManager() ! =null) {
         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }
   else {
      invokeAwareMethods(beanName, bean);
   }
   / / BeanPostProcessor processing
   Object wrappedBean = bean;
   if (mbd == null| |! mbd.isSynthetic()) {/ / callback postProcessBeforeInitialization method
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, 
																beanName);
   }
   try {
      // Process the initMethod specified in InitializingBean and BeanDefinition
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
           throw newBeanCreationException( (mbd ! =null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null| |! mbd.isSynthetic()) {/ / callback postProcessAfterInitialization method
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, 
															   beanName);
   }
   return wrappedBean;
}

Copy the code

Can be seen from the above source code is first deal with the interface of partially Aware, then followed by processing the BeanPostProcessor postProcessBeforeInitialization method, the method details are as follows:

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
   Object result = existingBean;
   // Process the BeanPostProcessor in turn
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      // If null is returned, the subsequent BeanPostProcessor is returned directly
	   / / postProcessBeforeInitialization no longer
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}
Copy the code

Details of the method is to perform postProcessBeforeInitialization callback content, can know from the implementation, BeanPostProcessor there can be multiple, and will be handled in accordance with the sequence. If as long as any one of them returns null, the subsequent BeanPostProcessor postProcessBeforeInitialization will no longer deal with.

Then the initialization method, invokeInitMethods, is invoked.

protected void invokeInitMethods(String beanName, Object bean, @Nullable 
	RootBeanDefinition mbd)
      throws Throwable {
   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 the current Bean implements the InitializingBean interface, its afterPropertiesSet() method is executed
      if(System.getSecurityManager() ! =null) {
         try {
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
            throwpae.getException(); }}else{ ((InitializingBean) bean).afterPropertiesSet(); }}// If initMethod is defined in BeanDefinition, the initialization method is performed
   if(mbd ! =null&& bean.getClass() ! = NullBean.class) { String initMethodName = mbd.getInitMethodName();if(StringUtils.hasLength(initMethodName) && ! (isInitializingBean &&"afterPropertiesSet".equals(initMethodName)) && ! mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); }}}Copy the code
  • From the above code is further validated the BeanPostProcessor postProcessBeforeInitialization method is invoked before initialization.

  • When executed after then invokeInitMethods applyBeanPostProcessorsAfterInitialization method.


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

Copy the code

The method with applyBeanPostProcessorsBeforeInitialization is almost the same, different is that it is postProcessAfterInitialization of execution. The initialization of the Spring Bean is now complete.

Support from 🍃 @postconstruct

You can see the initialization process in the Spring Bean lifecycle above, but Spring actually supports JSR250, such as the @postConstruct annotation, which is not found in the previous source code.

The secret is our BeanPostProcessor.

In Spring have a CommonAnnotationBeanPostProcessor class, this class has said in comments to this class is used to support for JSR and other specifications.

I’ll use the source code for this class to illustrate how Spring supports @PostContruct via BeanPostProcessor.

We can see from the above, CommonAnnotationBeanPostProcessor did not directly to BeanPostProcessor implementations, it inherits InitDestroyAnnotationBeanPostProcessor this class, The implementation of @PostConstruct is mostly in this class.

The implementation code for BeanPostProcessor is as follows:

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   // Lifecycle metadata encapsulation
   LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
   try {
      / / InitMethods execution
      metadata.invokeInitMethods(bean, beanName);
   }
   catch (InvocationTargetException ex) {
      throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
   }
   return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   return bean;
}
Copy the code

The implementation of BeanPostProcessor is mainly in the before method, which consists of two parts. The first part mainly encapsulates the information into LifecycleMetadata to facilitate the implementation of related initialization methods in the second step.

Through the above method implementation we know that Spring implementation of JSR250 by BeanPostProcessor to achieve.


public class BeanPostProcessorDemo2 {
    public static void main(String[] args) {
        // Create the base container
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // Build BeanDefinition and register it
        AbstractBeanDefinition beanDefinition = 
			BeanDefinitionBuilder.genericBeanDefinition(Person.class)
                .getBeanDefinition();
        beanFactory.registerBeanDefinition("person",beanDefinition);
        / / register CommonAnnotationBeanPostProcessor
        CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor 
			= new CommonAnnotationBeanPostProcessor();
        beanFactory.addBeanPostProcessor(commonAnnotationBeanPostProcessor);
        / / get a BeanPerson person = beanFactory.getBean(Person.class); System.out.println(person); }}class Person{
    @PostConstruct
    public void annotationInitMethod(a){
        System.out.println("@PostConstruct"); }}Copy the code

The above code is relatively simple. We define a Person and mark its initialization method with @postConstruct. Then we create a BeanFactory and create the BeanDefinition of Person and register it with the BeanFactory (just like reading the configuration file). Then we create CommonAnnotationBeanPostProcessor and add it to the BeanFactory.

  • And then print the result and print @postconstruct. If we comment the following line of code.
  • beanFactory.addBeanPostProcessor(commonAnnotationBeanPostProcessor);
  • When executed again, @postconstruct will fail and no result will be printed.

🍃 sequentiality

BeanPostProcessors can be registered with multiple processors. Inside AbstractBeanFactory, beanPostProcessors are stored in the List variable beanPostProcessors. The beanPostProcessors in the List are executed one by one, so we need to pay attention to the order when adding beanPostProcessors to the container. If we define multiple Beanpostprocessors in code or configuration files instead of manually adding them (most of the time not), we can control their order by implementing the Ordered interface.

BeanPostProcessor depends on beans that do not execute BeanPostProcessor, This is because BeanPostProcessor needs to be initialized before the BeanPostProcessor is initialized before the BeanPostProcessor is created.


In addition, we need to know: @postconstruct execution point (beforeInitialization) before afterProperitesSet(InvokeinitMethod-1) Prior to the execution of the initMethod (InvokeInitiMethod-2) method of the corresponding Bean definition.

Example code is as follows:

public class App3 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new 
		AnnotationConfigApplicationContext();
        context.scan("com.buydeem.beanpostprocessor"); context.register(App3.class); context.refresh(); }}@Component
class ClassA{}@Component
class ClassB{}@Component
class MyBeanPostProcessor implements BeanPostProcessor{
    @Autowired
    private ClassA classA;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
		throws BeansException {
        System.out.println("MyBeanPostProcessor"+bean);
        returnbean; }}Copy the code

Note: ClassA will not print, while ClassB will print. Because MyBeanPostProcessor relies on ClassA instances.

🍃 summary

There are many subinterfaces or implementation classes for BeanPostProcessor in Spring, for example.

InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor and so on.

These interfaces are at different stages of the Spring Bean life cycle, and their functionality is similar to that of BeanPostProcessor in providing extension points to Spring Bean declaration cycles.