BeanPostProcessor interface of Spring extension

Posted by jnewing on Wed, 05 Jan 2022 09:15:17 +0100

catalogue

1, Definition

2, Implementation class of custom BeanPostProcessor

3, Registration process analysis of BeanPostProcessor

4, Analysis of execution process of BeanPostProcessor

5, Common subclasses and implementation classes of BeanPostProcessor

6, Summary

1, Definition

BeanPostProcessor is an extension interface provided to us by the Spring IOC container. It allows us to customize and modify a new bean instance after the spring container instantiates the bean and before and after executing the bean initialization method, such as modifying the bean properties, generating a dynamic proxy instance for the bean, etc, The underlying processing of Spring AOP also implements the proxy wrapper logic by implementing BeanPostProcessor.

BeanPostProcessor is an interface, which is defined as follows:

public interface BeanPostProcessor {

	// After instantiation and dependency injection, execute before bean initialization
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// Execute after bean initialization
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

Both methods in the interface must return the incoming bean instead of null. If NULL is returned, the subsequent BeanPostProcessor will not be executed, or the target bean object may not be obtained through the getBean method.

2, Implementation class of custom BeanPostProcessor

(1) Write a simple entity class first

public class Student implements Serializable, InitializingBean {

	private String id;

	private String name;

	private Integer age;

	public Student() {
		System.out.println("implement Student instantiation ...");
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public void init() {
		System.out.println("implement Student of init()method...");
	}

	public void destroy() {
		System.out.println("implement Student of destroy()method...");
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("implement Student of afterPropertiesSet()method...");
	}

	@Override
	public String toString() {
		return "Student{" +
				"id='" + id + '\'' +
				", name='" + name + '\'' +
				", age=" + age +
				'}';
	}
}

Here, the entity class implements the InitializingBean interface, rewrites the afterpropertieset () method, and defines two corresponding methods for user-defined initialization and destruction.

(2) . write the implementation class of BeanPostProcessor

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("implement MyBeanPostProcessor of postProcessBeforeInitialization()method...");
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("implement MyBeanPostProcessor of postProcessAfterInitialization()method...");
		return bean;
	}
}

Like BeanFactoryPostProcessor, when defining multiple beanpostprocessors, if you want to customize the execution order of each BeanPostProcessor, you can also implement the Orderd interface to execute the value of the order attribute. The smaller the order, the higher the priority, and the BeanPostProcessor will be executed first.

(3) Write spring configuration files and register custom beanfactoryprocessor and bean information

<!--custom BeanPostProcessor-->
<bean id="student" class="com.wsh.beanpostprocessor.Student" init-method="init" destroy-method="destroy">
    <property name="id" value="1"/>
    <property name="name" value="Zhang San"/>
    <property name="age" value="10"/>
</bean>
<bean id="myBeanPostProcessor" class="com.wsh.beanpostprocessor.MyBeanPostProcessor"/>

(4) . test class

public class Test {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
		System.out.println( applicationContext.getBean("student"));
	}
}

(5) Operation results

implement Student instantiation ...
implement MyBeanPostProcessor of postProcessBeforeInitialization()method...
implement Student of afterPropertiesSet()method...
implement Student of init()method...
implement MyBeanPostProcessor of postProcessAfterInitialization()method...
Student{id='1', name='Zhang San', age=10}

It can be seen from the above results that before and after calling the two initialization methods, the logic of the custom BeanPostProcessor is executed. Here is only a simple output, which can be extended by the reader. The execution order of the initialization method is after propertieset (), and then the user-defined initialization method init method.

3, Registration process analysis of BeanPostProcessor

The registration of the BeanPostProcessor is completed in step 6 of the refresh() method, mainly through the registerbanpostprocessors (beanfactory) method:

/**
  * 6,Register the BeanPostProcessor. Note that the enhancement method corresponding to the BeanPostProcessor will not be executed here
  *  The real call is before and after bean initialization
  */
registerBeanPostProcessors(beanFactory);

// org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

As you can see, the core logic is delegated to the registerBeanPostProcessors() method of PostProcessorRegistrationDelegate.

First, briefly describe the processing process of the registerBeanPostProcessors() method:

  • 1. Obtain all beans that implement the BeanPostProcessor interface from the bean factory;
  • 2. Loop through all the scanned beanpostprocessors, and then store them in the previously defined collections;
  • 3. After sorting by priority, register the BeanPostProcessor that implements the PriorityOrdered interface;
  • 4. After sorting by priority, register the BeanPostProcessor that implements the Ordered interface;
  • 5. Register other ordinary beanpostprocessors;
public static void registerBeanPostProcessors(
    ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

    // 1. Get all beans that implement the BeanPostProcessor interface from the bean factory
    // As in this example, our custom MyBeanPostProcessor will be scanned here
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

    // Register BeanPostProcessorChecker that logs an info message when
    // a bean is created during BeanPostProcessor instantiation, i.e. when
    // a bean is not eligible for getting processed by all BeanPostProcessors.
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;

    // Add a beanpostprocessor - > beanpostprocessorchecker to the bean factory
    // BeanPostProcessorChecker is a BeanPostProcessor that records information messages during bean creation
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

    // Stores the BeanPostProcessor that implements the PriorityOrdered interface
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();

    // Stores the BeanPostProcessor that implements the MergedBeanDefinitionPostProcessor interface
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();

    // Stores the BeanPostProcessor that implements the Ordered interface
    List<String> orderedPostProcessorNames = new ArrayList<>();

    // Store ordinary BeanPostProcessor
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();

    // 2. Loop through all the scanned beanpostprocessors, and then store them in the previously defined collections
    for (String ppName : postProcessorNames) {

        // BeanPostProcessor that implements the PriorityOrdered interface
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            // Get the corresponding Bean instance through getBean and add it to the priorityordedpostprocessors collection
            // This involves the process of obtaining bean s, which will not be analyzed in depth for the time being
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);

            // If the MergedBeanDefinitionPostProcessor interface is also implemented, add it to the internalPostProcessors collection
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            // BeanPostProcessor that implements the Ordered interface
            orderedPostProcessorNames.add(ppName);
        }
        else {
            // Ordinary BeanPostProcessor
            nonOrderedPostProcessorNames.add(ppName);
        }
    }

    // sort
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);

    // 3. Register the BeanPostProcessor that implements the PriorityOrdered interface
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);

        // If the MergedBeanDefinitionPostProcessor interface is also implemented, add it to the internalPostProcessors collection
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    // sort
    sortPostProcessors(orderedPostProcessors, beanFactory);

    // 4. Register the BeanPostProcessor that implements the Ordered interface
    // Via beanfactory Addbeanpostprocessor (postprocessor) add BeanPostProcessor
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);

    // Now, register all regular BeanPostProcessors.

    // 5. Register other ordinary BeanPostProcessor
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);

        // If the MergedBeanDefinitionPostProcessor interface is also implemented, add it to the internalPostProcessors collection
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    // Via beanfactory Addbeanpostprocessor (postprocessor) add BeanPostProcessor
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

    // sort
    sortPostProcessors(internalPostProcessors, beanFactory);

    // Via beanfactory Addbeanpostprocessor (postprocessor) add BeanPostProcessor
    // 6. Register all internal beanpostprocessors
    registerBeanPostProcessors(beanFactory, internalPostProcessors);

    // Re-register post-processor for detecting inner beans as ApplicationListeners,
    // moving it to the end of the processor chain (for picking up proxies etc).

    // 7. Add a beanpostprocessor - > applicationlistenerdetector to the bean factory
    // ApplicationListenerDetector mainly detects whether the bean implements the ApplicationListener interface
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

The real registration method is in registerBeanPostProcessors():

// org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanPostProcessor>)
// Registers the given BeanPostProcessor
private static void registerBeanPostProcessors(
    ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {

    // Traverse the given BeanPostProcessor set, add it to the bean factory one by one through the addBeanPostProcessor method, and save it to the beanPostProcessors member variable of BeanFactory
    // private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
    for (BeanPostProcessor postProcessor : postProcessors) {
        beanFactory.addBeanPostProcessor(postProcessor);
    }
}

Specifically, loop the registered BeanPostProcessor collection through BeanFactory Addbeanpostprocessor is added and saved in beanPostProcessors member variable of BeanFactory:

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

// org.springframework.beans.factory.support.AbstractBeanFactory#addBeanPostProcessor
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
    // If the current bean postprocessor already exists, remove it first
    this.beanPostProcessors.remove(beanPostProcessor);

    // Track whether it is instantiation/destruction aware
    if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
        this.hasInstantiationAwareBeanPostProcessors = true;
    }
    if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
        this.hasDestructionAwareBeanPostProcessors = true;
    }

    // Add beanPostProcessors to the beanPostProcessors member property
    // private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
    this.beanPostProcessors.add(beanPostProcessor);
}

Through the above processing, the BeanPostProcessor has completed the registration with the bean factory. Note that the registration is only completed here, and the BeanPostProcessor is not executed. The real execution is before and after bean initialization.

4, Analysis of execution process of BeanPostProcessor

The above figure is a schematic diagram of Spring IOC container refresh. It can be seen from the figure that the execution of BeanPostProcessor is performed before and after bean initialization. Before bean initialization, the postProcessBeforeInitialization() method of BeanPostProcessor will be executed, and the postProcessAfterInitialization() method of BeanPostProcessor will be called after bean initialization.

As we all know, bean initialization is performed in AbstractAutowireCapableBeanFactory#initializeBean(). Let's start with

Start with the initializeBean() method to see how the BeanPostProcessor executes.

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 1. Execute Aware methods, such as BeanNameAware, BeanClassLoaderAware, beanfactory Aware
    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()) {
        // 2. Execute the pre-processing method of the post processor of the BeanPostProcessor: postProcessBeforeInitialization(), which allows you to wrap the bean instance
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 3. Execute initialization methods, including the afterpropertieset() method of InitializingBean and the user-defined initialization method init method
        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()) {
        // 4. Execute the post-processing method of the BeanPostProcessor post processor: postProcessAfterInitialization(), which allows the bean instance to be wrapped
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

The execution process of initializeBean() method is still very clear:

  • 1. Execute Aware methods, such as BeanNameAware, BeanClassLoaderAware, beanfactory Aware;
  • 2. Execute the pre-processing method of the post processor of the BeanPostProcessor: postProcessBeforeInitialization(), which allows the bean instance to be wrapped;
  • 3. Execute initialization methods, including the afterpropertieset() method of InitializingBean and the user-defined initialization method init method;
  • 4. Execute the post-processing method of the BeanPostProcessor post processor: postProcessAfterInitialization(), which allows the bean instance to be wrapped;

As you can see from the previous code, the execution of BeanPostProcessor is indeed in front of and behind the initialization method executed by the bean.

The pre enhancement of BeanPostProcessor is in the method applybeanpopostprocessorsbeforeinitialization():

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    // 1. Get all BeanPostProcessor post processors registered in the current factory
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 2. Execute the pre enhancement method of each BeanPostProcessor: postProcessBeforeInitialization()
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            // 3. If postProcessBeforeInitialization() returns null, the original bean will be returned directly, and subsequent enhancements to the BeanPostProcessor post processor will not be performed
            return result;
        }
        // 4. Use the enhanced bean current, assign it to result, and then return
        result = current;
    }
    return result;
}

The post enhancement of BeanPostProcessor is specifically in the method applybeanpopostprocessorsafterinitialization():

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    // 1. Get all BeanPostProcessor post enhancers registered in the factory
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 2. Execute the post enhancement method postProcessAfterInitialization() of BeanPostProcessor
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            // 3. If the return of postProcessAfterInitialization() is null, the original bean will be returned directly, and subsequent enhancements to the post processor of BeanPostProcessor will not be performed
            return result;
        }
        // 4. Use the enhanced bean current, assign it to result, and then return
        result = current;
    }
    return result;
}

The above is the specific execution process of BeanPostProcessor, which is relatively easy to understand.

5, Common subclasses and implementation classes of BeanPostProcessor

Spring has built-in BeanPostProcessor implementation classes and subclasses, such as:

  • (1) , instantiaawarebeanpostprocessor

Instantiawarebeanpostprocessor is a sub interface of BeanPostProcessor. It can provide extended callback interfaces in the other two periods of the Bean life cycle, that is, before instantiating the Bean (calling the postprocessbeforeinstance method) and after instantiating the Bean (calling the postprocessafterinstance method). The interface is defined as follows:

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

	// 1. Execute before instantiating the target Bean
    // 2. If the method returns a non empty object, the subsequent Spring default creation process will be skipped
    // 3. The default implementation returns null
	@Nullable
	default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		return null;
	}

	// 1. Execute after the target Bean is instantiated and before the attribute is filled
    // 2. The default implementation returns true
    // 3. If the method returns false, the subsequent property filling process will be skipped. Normally, it should return true
	default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		return true;
	}

	// Allow processing of properties before filling (such as validation of properties)
	@Nullable
	default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
			throws BeansException {

		return null;
	}

	// Modify the property values, and add or delete specific values by creating a new MutablePropertyValues instance based on the original PropertyValues
	// The method that has been marked as expired may be deleted in the future version of spring. The official recommendation is to use the postProcessProperties() method
    @Deprecated
	@Nullable
	default PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {

		return pvs;
	}

}
  • (2) ApplicationContextAwareProcessor: used to inject container objects such as ApplicationContext into bean s

In the postProcessBeforeInitialization() method, inject the specified attribute value into the target bean through the invokeAwareInterfaces(bean) method:

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    AccessControlContext acc = null;

    if (System.getSecurityManager() != null &&
        (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
         bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
         bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    // Execute Aware interface
    if (acc != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareInterfaces(bean);
            return null;
        }, acc);
    }
    else {
        invokeAwareInterfaces(bean);
    }

    return bean;
}

private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof Aware) {
        // Set the Environment property on the bean
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        // Set the EmbeddedValueResolver property on the bean
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }
        // Set the ResourceLoader property on the bean
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        // Set the ApplicationEventPublisher property on the bean
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        // Set the MessageSource property on the bean
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        // Set the ApplicationContext property on the bean
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}
  • (3) . ApplicationListenerDetector: judge whether the target bean is of ApplicationListener type. If so, it will be added to the event multicast

The function of registering the application monitor is implemented in the postProcessAfterInitialization() method:

public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof ApplicationListener) {
        // potentially not detected as a listener by getBeanNamesForType retrieval
        Boolean flag = this.singletonNames.get(beanName);
        if (Boolean.TRUE.equals(flag)) {
            // singleton bean (top-level or inner): register on the fly
            this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
        }
        else if (Boolean.FALSE.equals(flag)) {
            if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
                // inner bean with other scope - can't reliably process events
                logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                            "but is not reachable for event multicasting by its containing ApplicationContext " +
                            "because it does not have singleton scope. Only top-level listener beans are allowed " +
                            "to be of non-singleton scope.");
            }
            this.singletonNames.remove(beanName);
        }
    }
    return bean;
}
  • (4) CommonAnnotationBeanPostProcessor: supports @ Resource annotation injection
  • (5) . Autowired annotation beanpostprocessor: supports @ Autowired annotation injection
  • (6) . RequiredAnnotationBeanPostProcessor: supports @ Required annotation injection

6, Summary

To summarize the differences between beanfactorypost processor and BeanPostProcessor, see the following figure:

  • 1. Different execution times

BeanFactoryPostProcessor is called before bean is instantiated, that is, bean has not called the constructor before creating objects.

BeanPostProcessor is called before and after bean bean initialization. At this time, the bean has completed instantiation and property filling;

  • 2. Different processing objects

Beanfactoryprocessor is a BeanFactory level process that processes the entire Bean factory;

BeanPostProcessor refers to bean level processing, which is conducted for a specific bean;

  • 3. Different usage scenarios

Beanfactoryprocessor allows us to read the bean definition (configuration metadata) and modify it before instantiating any bean, or inject more bean definitions into the factory;

BeanPostProcessor allows us to customize and modify new bean instances, such as modifying bean properties or generating a dynamic proxy object for beans, etc;

Topics: Spring