1. The background

The core idea of Spring is that the container, when refreshed, looks calm on the outside, but inside is a sea of water. Springboot encapsulates Spring, following convention more than configuration, plus an auto-assembly mechanism. Most of the time we just reference a dependency, almost zero configuration can complete a function assembly.

I love this autowiring mechanism so much that I use it when DEVELOPING my own middleware and common dependency tools. Allow users to access with minimal cost. To get the autowiring thing going, you must understand spring’s construction lifecycle for beans and the various extension interfaces. Of course, understanding the various life cycles of beans also helps us deepen our understanding of Spring. Business code can also take advantage of these extension points to write better code.

A web search for Spring extension points turns up few blog posts that cover everything, just a few descriptions of commonly used extension points.

So in this article, I’ve summarized almost all of Spring & Springboot’s extension interfaces, as well as the usage scenarios for each extension point. It also draws up a sequential call diagram for all extensibility points of a bean within Spring from its loading to its final initialization. We can also see how the bean is loaded step by step into the Spring container.

2. Extensible interface start call sequence diagram

The following is the order of invocation of all extensible points in the life cycle of beans in the Spring container that I’ve compiled, which I’ll examine one by one

3.ApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer

It is the spring container prior to refresh the initialization ConfigurableApplicationContext callback interface, in simple terms, is to call before the container to refresh the initialize method. This point can be extended by the user himself. The user can do some things before the entire Spring container is initialized.

One possible scenario is to activate some configuration initially, or to perform dynamic bytecode injection while the class is not being loaded by the class loader.

Expansion mode:

public class TestApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("[ApplicationContextInitializer]"); }}Copy the code

Since the Spring container has not yet been initialized, there are three ways to make your extension work:

springApplication.addInitializers(new TestApplicationContextInitializer())
context.initializer.classes=com.example.demo.TestApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
Copy the code

4.BeanDefinitionRegistryPostProcessor

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

This interface is executed after reading the beanDefinition in the project, providing a supplementary extension point

Usage scenario: This is where you can dynamically register your beanDefinition and load beans outside of your classpath

Expansion mode:

public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanDefinitionRegistry");
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory"); }}Copy the code

5.BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor

This interface is an extension of the beanFactory interface and is called after Spring has read the beanDefinition information and before the bean is instantiated.

At this point, users can take care of things themselves by implementing the extension interface, such as modifying the meta information of registered BeanDefinitions.

Expansion mode:

public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("[BeanFactoryPostProcessor]"); }}Copy the code

6.InstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

This interface inherits the BeanPostProcess interface with the following differences:

BeanPostProcess interface in the initialization phase bean expanded (injection) before and after the spring context, and InstantiationAwareBeanPostProcessor interface on the basis of increased the three methods, Extensibility has been added to the instantiation phase and property injection phase.

The main extension points of this class include the following five methods, which are mainly in the two major phases of the bean life cycle: the instantiation phase and the initialization phase, which are explained together in the following order:

  • PostProcessBeforeInstantiation: before instantiation bean, equivalent to a new before the bean
  • PostProcessAfterInstantiation: after instantiation bean, equivalent to a new after the bean
  • PostProcessPropertyValues: bean is instantiated, inject phase trigger in the properties, the @autowired, @ Resource annotation principle based on this method, etc
  • PostProcessBeforeInitialization: before initialization bean, equivalent to inject beans before the spring context
  • After initialization postProcessAfterInitialization: bean, equivalent to the bean into the spring context

Usage scenario: This extension point is very useful for both writing middleware and business applications. Such as collecting beans that implement a particular type of interface across their lifetimes, or setting values uniformly for a particular type of bean, and so on.

Expansion mode:

public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
        returnbean; } @Override public Object postProcessBeforeInstantiation(Class<? > beanClass, String beanName) throws BeansException { System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
        return pvs;
    }
Copy the code

7.SmartInstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

The extension interface has three trigger point methods:

  • PredictBeanType: The trigger point occurs before postProcessBeforeInstantiation (did not indicate on the diagram, because generally don’t need to extend this point), the method used to predict the type of Bean, return the first Class type to success, if you can’t predict returns null; When you call beanFactory.getType (name), the callback method is called to determine the type information if the bean type information cannot be obtained from the bean name.
  • DetermineCandidateConstructors: after the trigger point in postProcessBeforeInstantiation, used to determine the bean constructors, return is a list of all constructors for the bean. The user can extend this point by customizing and selecting the appropriate constructor to instantiate the bean.
  • GetEarlyBeanReference: The trigger point occurs after postProcessAfterInstantiation, when there is a circular dependencies scene, when the bean is instantiated, in order to prevent a circular dependencies, exposed the callback method in advance, used for post processing bean instantiation. This method is triggered in the exposed callback method.

Expansion mode:

public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor { @Override public Class<? > predictBeanType(Class<? > beanClass, String beanName) throws BeansException { System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
        returnbeanClass; } @Override public Constructor<? >[] determineCandidateConstructors(Class<? > beanClass, String beanName) throws BeansException { System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
        return null;
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
        returnbean; }}Copy the code

8.BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware

This class has only one trigger point, which occurs after the bean is instantiated and before the property is injected, that is, before the Setter. The extension point method for this class is setBeanFactory, which gets the BeanFactory property.

In this scenario, you can take the BeanFactory after the bean is instantiated, but before it is initialized, and at this point, you can make specific customization for each bean. Or you can cache the BeanFactory for later use.

Expansion mode:

public class TestBeanFactoryAware implements BeanFactoryAware {
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("[TestBeanFactoryAware] "+ beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName()); }}Copy the code

9.ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor

The class itself does not have extension points, but there are six extension points that can be implemented within the class. These classes fire after the bean is instantiated but before it is initialized

As you can see, this class is used to execute the various driver interfaces to retrieve the corresponding container variables by executing the extension interfaces highlighted in the red box above after the bean is instantiated and the properties are populated. So there should be six extension points here, just put them all together

  • EnvironmentAware: An extended class for getting to EnviromentAware, a useful variable to get all the parameters in the system. Of course, I don’t think it is necessary to extend Aware, because spring can be accessed directly through injection.
  • EmbeddedValueResolverAware: StringValueResolver is an extension of the class that gets StringValueResolver. StringValueResolver is used to get variables based on String properties. If we cache StringValueResolver and use this class to fetch a String variable, we get the same effect.
  • ResourceLoaderAware: An extension class for obtaining ResourceLoader, which can be used to obtain all resource objects in the classpath. You can extend this class to obtain ResourceLoader objects.
  • ApplicationEventPublisherAware: Used to get an extension of ApplicationEventPublisher class, ApplicationEventPublisher can be used to publish events, combined with ApplicationListener to common use, More on this later in the introduction to ApplicationListener. This object can also be obtained via Spring injection.
  • MessageSourceAware: An extended class for retrieving MessageSource, which is primarily used for internationalization.
  • ApplicationContextAware: An extension class that gets the ApplicationContext, which many of you are familiar with, is the Spring Context Manager. You can manually get any bean registered in the Spring context. We often extend this interface to cache the Spring context, wrapped as static methods. ApplicationContext also implements the BeanFactory MessageSource, ApplicationEventPublisher interface, can also be used to do the relevant interface.

10.BeanNameAware

org.springframework.beans.factory.BeanNameAware

You can see, this class is also Aware an extension, trigger point before the initialization of bean, namely postProcessBeforeInitialization before this class the trigger method is only one: setBeanName

The user can extend this point to get the beanName registered in the Spring container before initializing the bean and modify the value of the beanName.

Expansion mode:

public class NormalBeanA implements BeanNameAware{
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("[BeanNameAware] "+ name); }}Copy the code

11.@PostConstruct

javax.annotation.PostConstruct

This is not an extension point, but rather a annotation. At the bean initialization stage, if a method is labeled @postconstruct, this method will be called first. Under the key here is to focus on the trigger point of this standard, the trigger point is after postProcessBeforeInitialization InitializingBean. AfterPropertiesSet before.

Usage scenario: Users can annotate a method to initialize a property

Expansion mode:

public class NormalBeanA {
    public NormalBeanA() {
        System.out.println("NormalBean constructor");
    }

    @PostConstruct
    public void init(){
        System.out.println("[PostConstruct] NormalBeanA"); }}Copy the code

12.InitializingBean

org.springframework.beans.factory.InitializingBean

This class, as the name suggests, is also used to initialize beans. The InitializingBean interface provides a way for beans to initialize methods, including only afterPropertiesSet methods, which all classes that inherit the interface execute when initializing the bean. The extension point before postProcessAfterInitialization trigger.

Usage scenario: Users implement this interface to initialize some service indicators when the system starts.

Expansion mode:

public class NormalBeanA implements InitializingBean{
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[InitializingBean] NormalBeanA"); }}Copy the code

13.FactoryBean

org.springframework.beans.factory.FactoryBean

In general, Spring uses reflection to specify branch classes to instantiate beans using the bean’s class attribute. In some cases, the instantiation of beans is complicated and requires a lot of configuration information to be provided in the bean in the traditional way. The flexibility of configuration is limited, and coding may yield a simple solution. Spring provides a org. Springframework. Beans. Factory. FactoryBean factory class interface, users can customize by implementing the interface logic instantiation of the bean. The FactoryBean interface plays an important role in the Spring framework. Spring itself provides over 70 FactoryBean implementations. They hide the details of instantiating complex beans for the benefit of upper-layer applications. Starting with Spring3.0, factorybeans began to support generics, that is, interface declarations changed to the form of factorybeans

Usage scenario: Users can extend this class to act as a proxy for the bean to be instantiated, such as intercepting all methods of the object, and output a log line before and after the call, mimicking the functionality of ProxyFactoryBean.

Expansion mode:

public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {

    @Override
    public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception {
        System.out.println("[FactoryBean] getObject");
        returnnew TestFactoryBean.TestFactoryInnerBean(); } @Override public Class<? >getObjectType() {
        return TestFactoryBean.TestFactoryInnerBean.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public static class TestFactoryInnerBean{

    }
}
Copy the code

14.SmartInitializingSingleton

org.springframework.beans.factory.SmartInitializingSingleton

AfterSingletonsInstantiated this interface is only one method, its role is in the spring container management all the singleton object (not lazy loading) initialization is completed calls the callback interface. After the trigger is postProcessAfterInitialization.

Usage scenario: Users can extend this interface to perform some post-processing after initializing all singletons.

Expansion mode:

public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("[TestSmartInitializingSingleton]"); }}Copy the code

15.CommandLineRunner

org.springframework.boot.CommandLineRunner

This interface also has only one method: run(String… Args), the trigger time is automatically executed after the whole project is started. If you have more than one CommandLineRunner, you can use @Order to sort it.

Usage scenario: Users extend this interface to preprocess some services after starting the project.

Expansion mode:

public class TestCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        System.out.println("[TestCommandLineRunner]"); }}Copy the code

16.DisposableBean

org.springframework.beans.factory.DisposableBean

This extension point also has only one method: destroy(), which is triggered when the object is destroyed and automatically executed. Such as running applicationContext. RegisterShutdownHook, will trigger this method.

Expansion mode:

public class NormalBeanA implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("[DisposableBean] NormalBeanA"); }}Copy the code

17.ApplicationListener

org.springframework.context.ApplicationListener

To be precise, this should not be an extension point in Spring&SpringBoot. ApplicationListener can listen for an event that can be triggered during the execution of a business method. The user can customize a business event. But Spring also has some built-in events that can be interspersed with launch calls. We can also use this feature to make our own listeners for built-in events to do much the same thing as the previous triggers.

Here’s a list of spring’s main built-in events:

  • ContextRefreshedEventApplicationContext initialized or refresh, the incident was released. This can also be used in ConfigurableApplicationContext interface refresh () method to happen. Initialization here means that all beans are successfully loaded, the post-processing beans are detected and activated, all Singleton beans are pre-instantiated, and the ApplicationContext container is ready for use.
  • ContextStartedEvent when using ConfigurableApplicationContext (ApplicationContext interface) start () method in the interface of startup ApplicationContext, The event was released. You can investigate your database, or you can restart any stopped applications after receiving this event.
  • ContextStoppedEvent when using ConfigurableApplicationContext interface the stop () when they stop ApplicationContext release this event. You can do the necessary cleaning up after receiving the event
  • ContextClosedEvent when using ConfigurableApplicationContext close () method in the interface of closed ApplicationContext, the incident was released. A closed context reaches the end of its life cycle; It cannot be refreshed or restarted
  • RequestHandledEvent This is a Web-specific event that tells all beans that the HTTP request has been served. This parameter is applicable only to Web applications that use DispatcherServlet. When Spring is used as the front-end MVC controller, the system automatically fires this event when Spring finishes processing the user request

18. The last

From these spring&SpringBoot extension points, we can roughly peek into the entire bean life cycle. When developing a business or writing a middleware business, we can take advantage of the extension points spring provides and do something during the various stages of Spring startup. To achieve the purpose of custom initialization. If there are mistakes or omissions in this summary, please correct them.