Spring extension point -- BeanDefinition and BeanFactory extension

Keywords: Spring

 

1.BeanDefinitionRegistryPostProcessor interface

The BeanDefinitionRegistryPostProcessor interface inherits the beanfactory postprocessor interface, which we will talk about later.

/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.
 *
 * @author Juergen Hoeller
 * @since 3.0.1
 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

This interface extends the standard beanfactoryprocessor interface and allows more beandefinitions to be registered before the implementation class of the ordinary beanfactoryprocessor interface is executed. In particular, BeanDefinitionRegistryPostProcessor can register BeanDefinition of beanfactoryprocessor.

The postProcessBeanDefinitionRegistry method can modify any BeanDefinition registered in the BeanDefinitionRegistry interface implementation class, or add or delete BeanDefinition. The reason is that all conventional beandefinitions have been loaded into the BeanDefinitionRegistry interface implementation class before this method is executed, but no beans have been instantiated.

Interface application

We just need to write a class to implement the interface, and then configure this class into the xml configuration of spring. The following is a simple implementation class I wrote:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

public class DefaultBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        logger.info("BeanDefinitionRegistryPostProcessor of postProcessBeanDefinitionRegistry method,You can add, modify and delete here bean Definition of");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        logger.info("BeanDefinitionRegistryPostProcessor of postProcessBeanFactory method,You can be right here beanFactory Do something");
    }
}

In fact, org.Mybatis.spring.mapper.mappercannerconfigurer in Mybatis implements this method. When only the interface has no implementation class, find the relationship between the interface method and sql, so as to generate BeanDefinition and register. Spring's org.springframework.context.annotation.ConfigurationClassPostProcessor is also used to register the BeanDefinition corresponding to the method of generating beans in annotation @ Configuration.

2. Beanfactoryprocessor interface

After the BeanFactory is generated, what should I do if I want to process the BeanFactory? The beanfactoryprocessor interface is used to process BeanFactory.

/**
 * Allows for custom modification of an application context's bean definitions,
 * adapting the bean property values of the context's underlying bean factory.
 *
 * <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
 * their bean definitions and apply them before any other beans get created.
 *
 * <p>Useful for custom config files targeted at system administrators that
 * override bean properties configured in the application context.
 *
 * <p>See PropertyResourceConfigurer and its concrete implementations
 * for out-of-the-box solutions that address such configuration needs.
 *
 * <p>A BeanFactoryPostProcessor may interact with and modify bean
 * definitions, but never bean instances. Doing so may cause premature bean
 * instantiation, violating the container and causing unintended side-effects.
 * If bean instance interaction is required, consider implementing
 * {@link BeanPostProcessor} instead.
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see BeanPostProcessor
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

This interface allows you to customize and modify the BeanDefinition of the application context and adjust the bean property value of the BeanFactory of the context. The application context can automatically detect beanfactoryprocessor beans in the BeanDefinition of BeanFactory and apply them before creating any other beans. It is useful for custom configuration files located in the system administrator, which override the bean properties configured in the application context. See propertyresourceconfigurator and its implementation for out of the box solutions to address such configuration requirements. Beanfactoryprocessor may interact with and modify the bean definition, but the bean should never be instantiated. Doing so may lead to premature bean instantiation, violation of container execution order, and unexpected side effects. If you need bean instance interaction, consider implementing the BeanPostProcessor interface.

postProcessBeanFactory method after BeanFactory initialization, all bean definitions are loaded, but when no bean will be instantiated, it is allowed to rewrite or add properties.

Interface application

The DefaultBeanDefinitionRegistryPostProcessor interface can be implemented and configured into the xml configuration of spring.

The most commonly used application is org.springframework.beans.factory.config.PropertyPlaceholderConfigurer. After the BeanDefinition is generated, some parameters may be ${key}. This implementation class converts the previous parameters into the values corresponding to the keys in xxx.properties.

3. Extensions in bean instantiation

The previous section focuses on BeanDefinition and BeanFactory. Some specific interface implementation classes will be called during bean instantiation. What are these interfaces?

  InstantiationAwareBeanPostProcessor interface

/**
 * Subinterface of {@link BeanPostProcessor} that adds a before-instantiation callback,
 * and a callback after instantiation but before explicit properties are set or
 * autowiring occurs.
 *
 * <p>Typically used to suppress default instantiation for specific target beans,
 * for example to create proxies with special TargetSources (pooling targets,
 * lazily initializing targets, etc), or to implement additional injection strategies
 * such as field injection.
 *
 * <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
 * internal use within the framework. It is recommended to implement the plain
 * {@link BeanPostProcessor} interface as far as possible, or to derive from
 * {@link InstantiationAwareBeanPostProcessorAdapter} in order to be shielded
 * from extensions to this interface.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @since 1.2
 * @see org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#setCustomTargetSourceCreators
 * @see org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator
 */
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor <i>before the target bean gets instantiated</i>.
     * The returned bean object may be a proxy to use instead of the target bean,
     * effectively suppressing default instantiation of the target bean.
     * <p>If a non-null object is returned by this method, the bean creation process
     * will be short-circuited. The only further processing applied is the
     * {@link #postProcessAfterInitialization} callback from the configured
     * {@link BeanPostProcessor BeanPostProcessors}.
     * <p>This callback will only be applied to bean definitions with a bean class.
     * In particular, it will not be applied to beans with a "factory-method".
     * <p>Post-processors may implement the extended
     * {@link SmartInstantiationAwareBeanPostProcessor} interface in order
     * to predict the type of the bean object that they are going to return here.
     * @param beanClass the class of the bean to be instantiated
     * @param beanName the name of the bean
     * @return the bean object to expose instead of a default instance of the target bean,
     * or {@code null} to proceed with default instantiation
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.support.AbstractBeanDefinition#hasBeanClass
     * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName
     */
    Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;

    /**
     * Perform operations after the bean has been instantiated, via a constructor or factory method,
     * but before Spring property population (from explicit properties or autowiring) occurs.
     * <p>This is the ideal callback for performing custom field injection on the given bean
     * instance, right before Spring's autowiring kicks in.
     * @param bean the bean instance created, with properties not having been set yet
     * @param beanName the name of the bean
     * @return {@code true} if properties should be set on the bean; {@code false}
     * if property population should be skipped. Normal implementations should return {@code true}.
     * Returning {@code false} will also prevent any subsequent InstantiationAwareBeanPostProcessor
     * instances being invoked on this bean instance.
     * @throws org.springframework.beans.BeansException in case of errors
     */
    boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;

    /**
     * Post-process the given property values before the factory applies them
     * to the given bean. Allows for checking whether all dependencies have been
     * satisfied, for example based on a "Required" annotation on bean property setters.
     * <p>Also allows for replacing the property values to apply, typically through
     * creating a new MutablePropertyValues instance based on the original PropertyValues,
     * adding or removing specific values.
     * @param pvs the property values that the factory is about to apply (never {@code null})
     * @param pds the relevant property descriptors for the target bean (with ignored
     * dependency types - which the factory handles specifically - already filtered out)
     * @param bean the bean instance created, but whose properties have not yet been set
     * @param beanName the name of the bean
     * @return the actual property values to apply to the given bean
     * (can be the passed-in PropertyValues instance), or {@code null}
     * to skip property population
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.MutablePropertyValues
     */
    PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;

}

This interface is a sub interface of BeanPostProcessor. It is used to set the callback function before instantiation after instantiation but before setting explicit properties or automatic assembly. It is usually used to suppress the default instantiation of a specific target bean. For example, create an agent with special TargetSources (pooled targets, delayed initialization targets, etc.), or implement other injection strategies, such as field injection. Note: this interface is a special interface, which is mainly used for internal use within the framework. It is recommended to implement a simple BeanPostProcessor interface as much as possible, or derive from the instantiaawarebeanpostprocessoradapter, so as to mask the extension of this interface.

The postprocessbeforeinstance method creates a bean before the instantiation of the target bean. If a bean is created here, the default instantiation process will not be followed. It is usually used to create an agent. Note that the bean generated by the factory method will not take this method.

The postprocessafterinstance method is executed after the target bean is instantiated but before the property filling.

The postProcessPropertyValues method post processes the given property value after it is set to the given bean. Allows you to check whether all dependencies are met, such as based on the "Required" annotation on the bean property setter. It also allows you to replace the property values to be applied, usually by adding or deleting specific values by creating a new MutablePropertyValues instance based on the original PropertyValues.

Interface application

Spring does not recommend that users implement this interface directly. If they have to apply their own callback functions at these extension points, spring suggests that they inherit the instantiaawarebeanpostprocessoradapter and rewrite the corresponding methods.

org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator, which creates a proxy based on beanName, applies this interface to generate proxy beans before generating beans, so as to replace the default instantiation.

BeanPostProcessor interface

/**
 * Factory hook that allows for custom modification of new bean instances,
 * e.g. checking for marker interfaces or wrapping them with proxies.
 *
 * <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
 * bean definitions and apply them to any beans subsequently created.
 * Plain bean factories allow for programmatic registration of post-processors,
 * applying to all beans created through this factory.
 *
 * <p>Typically, post-processors that populate beans via marker interfaces
 * or the like will implement {@link #postProcessBeforeInitialization},
 * while post-processors that wrap beans with proxies will normally
 * implement {@link #postProcessAfterInitialization}.
 *
 * @author Juergen Hoeller
 * @since 10.10.2003
 * @see InstantiationAwareBeanPostProcessor
 * @see DestructionAwareBeanPostProcessor
 * @see ConfigurableBeanFactory#addBeanPostProcessor
 * @see BeanFactoryPostProcessor
 */
public interface BeanPostProcessor {

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     */
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    /**
     * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other BeanPostProcessor callbacks.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     */
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

This interface allows you to customize and modify new bean instances, such as checking the tag interface or wrapping with a proxy. Note that if there are interdependent beans, the proxy may not be used here.

The postProcessBeforeInitialization method applies this BeanPostProcessor to a given new bean instance before any bean initialization callback (such as the afterpropertieset of InitializingBean or the custom init method). The bean has been populated with property values. The returned bean instance may be the original wrapper.

The postProcessAfterInitialization method applies this BeanPostProcessor to the given new bean instance after the bean initialization callback (such as the afterpropertieset of InitializingBean or the custom init method). The bean has been populated with property values. The returned bean instance may be the original wrapper. This method will also prevent the instantiawarebeanpostprocessor #postprocessbeforeinstance method from generating objects again after it generates objects (for details, please refer to Spring bean generation process).

Interface application

org.springframework.context.support.ApplicationContextAwareProcessor injects the application context before the bean initializes the callback.

Other extension points

InitializingBean interface

After executing the postProcessBeforeInitialization method of BeanPostProcessor, if the bean implements the InitializingBean interface, it will call the afterpropertieset method.

Various Aware

Before executing the postProcessBeforeInitialization method of BeanPostProcessor, if the bean implements BeanNameAware, BeanClassLoaderAware or BeanFactoryAware, the interface related methods will be called, and the input parameters are the values concerned by the bean.

The ApplicationContextAwareProcessor is used to set the context through the aware related interfaces of environmentaware, embeddedvalueresolveaware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware and ApplicationContextAware if the bean has aware interfaces before initialization, Except for the embeddedvalueresolveaware interface ClassPathXmlApplicationContext, all the above interfaces are implemented, that is, the method input parameters calling these interfaces are ClassPathXmlApplicationContext. The input parameter of EmbeddedValueResolver aware is EmbeddedValueResolver, and its constructor input parameter is beanFactory in the context.

BeanDefinition entry extension

When defining beans, you can specify constructors, set properties, and set init method and destroy method. Constructor needless to say, the setting property is executed after the instantiawarebeanpostprocessor#postprocesspropertyvalues method, the init method is executed after the afterpropertieset method of InitializingBean, and the destroy method is called when the container is closed.

Injection of extension points in spring

In the first three sections, we learned a lot about extension points. How do the interfaces in these extension points work in spring? In other words, when are they called or added to BeanFactory to wait for calls?

Container level

We have to start with the abstract class AbstractApplicationContext#refresh method of the context

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        // Set the context start time and active tag, and load the attribute resource at the same time.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        // Stop the previously started beanFactory, if any, and generate a new beanFactory to load the BeanDefinition in the configuration.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        // Set class loader for beanFactory, add post processor 'ApplicationContextAwareProcessor', etc.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

The nvokebeanfactory postprocessors (beanfactory) method is the call to BeanDefinitionRegistryPostProcessor and beanfactory postprocessor mentioned in the first section.

Calling logic of invokebeanfactoryprocessors method:

If beanFactory is the implementation class of BeanDefinitionRegistry

Get all the implementation classes of the BeanFactoryPostProcessor interface in the input parameter, select the implementation class that implements the BeanDefinitionRegistryPostProcessor interface, and execute the postProcessBeanDefinitionRegistry method first.

Next, call the implementation class that implements the PriorityOrdered interface in the implementation class that implements the BeanDefinitionRegistryPostProcessor interface in beanFactory. Before calling these implementation classes, sort them according to the getOrder method of PriorityOrdered, and then call the postProcessBeanDefinitionRegistry method in order.

Next, call the class that implements the Ordered interface in the implementation class that implements the BeanDefinitionRegistryPostProcessor interface in beanFactory, and call the postProcessBeanDefinitionRegistry method in order.

Finally, we call the other postProcessBeanDefinitionRegistry methods to implement the BeanDefinitionRegistryPostProcessor interface in beanFactory.

Finally, first call the postProcessBeanFactory method of all the implementation classes that implement the BeanDefinitionRegistryPostProcessor interface in the input parameter, and then call the postProcessBeanFactory method of the implementation class that implements the beanfactory postprocessor interface in the input parameter.

If not, only call all the beanfactoryprocessor implementation classes entered into the parameter once.

After the above is completed, the bean that implements the beanfactoryprocessor interface (not executed) is obtained from beanFactory. It is also divided into three categories. The PriorityOrdered group is called first, followed by Ordered, and others are at the bottom.
Finally, clear the metaData cache of beanFactory (mainly the mapping cache of class and beanname)

The registerbanpostprocessors (beanfactory) method is the registration of the BeanPostProcessor interface implementation class in the second section.

Calling logic of registerBeanPostProcessors method:

Add BeanPostProcessorChecker first.

Then, the implementation class of BeanPostProcessor interface in beanFactory is divided into four parts and added to beanFactory respectively.

The PriorityOrdered part implements the PriorityOrdered interface and does not belong to the MergedBeanDefinitionPostProcessor

The Ordered part implements the Ordered interface and does not belong to the MergedBeanDefinitionPostProcessor

Others do not belong to MergedBeanDefinitionPostProcessor

Belonging to MergedBeanDefinitionPostProcessor

The PriorityOrdered and Ordered parts are sorted first, and then added to the beanporocessors property of beanFactory in the order above

bean level

Interface properties on other beans are called one by one during bean generation.

 

Posted by Javizy on Tue, 23 Nov 2021 14:59:16 -0800