The life cycle of Spring beans is really one of the most frequently asked questions about Spring in interviews. I have been stumped by this question more than once and have trouble remembering the long list of steps, but actually I am wrong to try to memorize the steps by rote, seemingly just reciting a process, in fact, This process involves a lot of knowledge but very interesting.

Here’s a picture I’m sure many of you have seen the same or similar one:

It seems pretty long, right, but we can actually break it down into the following four big steps:

  1. Instantiate the Bean
  2. Set object properties, dependency injection
  3. Initialize the
  4. Destroy: Bean destruction

Next, I will follow this step step by step and explain the important classes or interfaces in Spring from the point of view of the lifecycle.

Instantiate beans

In this first step, the container instantiates by getting information from the BeanDefinition object. And this step is just a simple instantiation without dependency injection, which can be understood as new xx(). Note, however, that there is a difference in instantiation between BeanFactory and ApplicationContext containers.

  • For BeanFactory containers, createBean() is called when a customer requests an uninitialized bean from the container, or when another uninitialized dependency needs to be injected when the bean is initialized.
  • For the ApplicationContext container, all beans are instantiated when the container is started.

Now that I’ve mentioned BeanFactory and ApplicationContext, I’ll give you a brief introduction to those interfaces as well. Respectively in the org. Springframework. Beans and org., springframework. The context under the two packages, and the two packages, it is the foundation of the Spring IoC container. BeanFactory provides some basic functions of the framework, including IOC configuration mechanism, providing various definitions of beans, establishing dependencies between beans, etc. ApplicationContext inherits from BeanFactory, so it has all the functionality of BeanFactory, but it also inherits some other interfaces, such as message resource configuration, event publishing, and so on.

BeanFactory is the Spring framework infrastructure for Spring. ApplicationContext is intended for developers of the Spring framework. So we will find that we are more likely to use ApplicationContext tools than BeanFactory tools.

In addition, before and after the instantiation of this step, in fact there is hidden tasks, called InstantiationAwareBeanPostProcessor involved in the interface. It inherits from BeanPostProcessor. For those of you who don’t know the BeanPostProcessor interface, I’m going to introduce you to this one.

BeanPostProcessor in org. Springframework. Beans, also known as post processor, which defines a series of callback method is used to make the user can customize the Bean is instantiated or dependency parsing logic. Itself only has two methods: postProcessBeforeInitialization and postProcessAfterInitialization. Define the Bean’s pre – and post-initialization logic, as indicated by the method name. This interface is associated throughout its life cycle.

public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        returnbean; }}Copy the code

Don’t confuse the methods in BeanPostProcessor I’m talking about before and after initialization, but the first step in the lifecycle is instantiation. Here use InstantiationAwareBeanPostProcessor it has its own two methods to instantiate the logical processes. PostProcessBeforeInstantiation and postProcessAfterInstantiation.

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
        return null;
    }

    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Nullable
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return null; }}Copy the code

By looking for call postProcessBeforeInstantiation this method, can trace back to create the key of the Bean method. Under the AbstractAutowireCapableBeanFactory createBean ().

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
	// The code inside is highlighted step by step
}
Copy the code

You can see how important this method is by looking at the comments in the source code. It can create instances of beans, populate them with properties, call BeanPostProcesser, and so on. The first code we notice should be

Class<? > resolvedClass = resolveBeanClass(mbd, beanName);Copy the code

It is actually one step ahead of our illustration of the life cycle above, which is to get the information in the BeanDefinition object to parse the Bean class. A BeanDefinition is a description of a Bean. It contains some basic information about the Bean, such as the Bean name, the scope of the Bean, the relationship with other beans, and so on. After parsing, proceed to the next step.

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if(bean ! =null) {
  return bean;
}
Copy the code

This annotation gives the beanPostprocessors an opportunity to return an instance of the proxy instead of the target bean. But we turn to the upper InstantiationAwareBeanPostProcessor for the realization of this method, will find that it returns null, so the default logic will not return. So we’ll go to the next step, doCreateBean.

Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
   logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
Copy the code

Once you enter this method, the first line of the comment is simply to instantiate the Bean.

// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
   instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Copy the code

As you can see, createBeanInstance() returns a BeanWrapper object. BeanWrapper is a wrapper around beans that can be set to get wrapped objects, property descriptors for wrapped beans, and so on. We don’t use this class directly in our development. CreateBeanInstance () generates a new instance of the specified bean using appropriate instantiation strategies, such as factory methods, constructor injection, or simple instantiation.

	/** * Create a new instance for the specified bean, using an appropriate instantiation strategy: * factory method, constructor autowiring, or simple instantiation. */
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // I will not paste the code
  }
Copy the code

The next piece of code, as noted in the comments, will call various postProcessers to merge properties, and there will be some scanning of annotations.

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
   if(! mbd.postProcessed) {try {
         applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      }
      catch (Throwable ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Post-processing of merged bean definition failed", ex);
      }
      mbd.postProcessed = true; }}Copy the code

Then there is a piece of code that is closely related to an interview question about how Spring resolves loop dependencies. Since the focus of this article is on the life cycle of beans, I was going to write it all together, but I realized that there is too much to say. I will write a separate article later on how to solve the problem of loop dependency.

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isTraceEnabled()) {
      logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Copy the code

Second, set object properties, dependency injection

After finishing the instantiation, we continue down the createBean method to the populateBean step.

// Initialize the bean instance.
Object exposedObject = bean;
try {
   populateBean(beanName, mbd, instanceWrapper);
   exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
   if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
      throw (BeanCreationException) ex;
   }
   else {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); }}Copy the code

For this method, we have begun to notice I said before is the instantiation is, there is a hidden mission before and after the above mentioned before instantiating postProcessBeforeInstantiation, so has been instantiated, Now after the instantiation method postProcessAfterInstantiation, here is also a chance to go to the custom for InstantiationAwareBeanPostProcessors attributes are way after the assignment. Go back to executing the post-processor method in later code.

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      if(! bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return; }}}Copy the code

Moving on, we see a familiar word, Autowire, but here’s how to get the current injection, depending on whether byName or byType is the property to be injected. I’m not going to go into that.

PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
   MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
   // Add property values based on autowire by name if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
      autowireByName(beanName, mbd, bw, newPvs);
   }
   // Add property values based on autowire by type if applicable.
   if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      autowireByType(beanName, mbd, bw, newPvs);
   }
   pvs = newPvs;
}
Copy the code

Then there is the method I mentioned in the first step of populateBean, which checksums the properties if there is a post-processor, and the second Boolean variable is used to determine whether a dependency check is performed.

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
booleanneedsDepCheck = (mbd.getDependencyCheck() ! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds =null;
if (hasInstAwareBpps) {
   if (pvs == null) {
      pvs = mbd.getPropertyValues();
   }
   for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
         if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
         }
         pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
         if (pvsToUse == null) {
            return; } } pvs = pvsToUse; }}if (needsDepCheck) {
   if (filteredPds == null) {
      filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
   }
   checkDependencies(beanName, mbd, filteredPds, pvs);
}
Copy the code

The last step is to inject the properties from the previous steps.

if(pvs ! =null) {
   applyPropertyValues(beanName, mbd, bw, pvs);
}
Copy the code

Initialization

After the populateBean() method ends, the initializeBean() method begins, which, as the method name implies, is the bean initialization. Note that initialization and instantiation are not the same thing; beans are instantiated before they are initialized during their life cycle.

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()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); }try {
      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()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); }return wrappedBean;
}
Copy the code

The first step is to focus on invokeAwareMethods(beanName, bean), regardless of the code for the security mechanism. This line of code, here is the interface that calls all Aware types. The Aware type interface in Spring allows us to retrieve some of the resources in the container.

private void invokeAwareMethods(String beanName, Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof BeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName);
      }
      if (bean instanceof BeanClassLoaderAware) {
         ClassLoader bcl = getBeanClassLoader();
         if(bcl ! =null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); }}if (bean instanceof BeanFactoryAware) {
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); }}}Copy the code

Such as it appears there are three types of Aware of interface: BeanNameAware, BeanClassLoaderAware, BeanFactoryAware. They do different things depending on the interface that the bean implements.

  • If the BeanNameAware interface is implemented, the beanName is set to the bean using the setBeanName() method
  • If the BeanClassLoaderAware interface is implemented, the ClassLoader is passed in via the setBeanClassLoader() method
  • If the BeanFactoryAware interface is implemented, the BeanFactory container instance is passed in via the setBeanFactory() method

After Aware of the call interface, continue to go down to the applyBeanPostProcessorsBeforeInitialization () this method (because if judgment is the bean is empty or if the bean is synthetic, Our bean does not, so the final part of the if condition will be true. This method is to handle I have mentioned before the BeanPostProcessor postProcessBeforeInitialization () method. This method is executed if the Bean implements the BeanPostProcessor interface.

The process then moves to the invokeInitMethods() step, which executes the bean’s custom initialization method. If the bean implements the InitializingBean interface, then the **afterPropertiesSet() method needs to be overridden. The afterPropertiesSet()** method is invoked in invokeInitMethods. If the bean has a custom init method, the specified init method is called (which can be done by configuring the bean’s init-method property in XML) in the order afterPropertiesSet comes first and custom init comes second.

Continue is applyBeanPostProcessorsAfterInitialization () method, and the previous applyBeanPostProcessorsBeforeInitialization () method before and after the echo, this won’t have to say more.

Four, destroy,

Behind a piece of code is related to processing circular dependencies, just not much said, directly to the last step, registerDisposableBeanIfNecessary ().

The key interface to DisposableBean is To DisposableBean, which is similar to the InitializingBean interface, init at the initialization stage and destroy at the end stage.

/**
 * Add the given bean to the list of disposable beans in this factory,
 * registering its DisposableBean interface and/or the given destroy method
 * to be called on factory shutdown (if applicable). Only applies to singletons.
 * @param beanName the name of the bean
 * @param bean the bean instance
 * @param mbd the bean definition for the bean
 * @see RootBeanDefinition#isSingleton
 * @see RootBeanDefinition#getDependsOn
 * @see #registerDisposableBean
 * @see #registerDependentBean
 */
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { AccessControlContext acc = (System.getSecurityManager() ! =null ? getAccessControlContext() : null);
   if(! mbd.isPrototype() && requiresDestruction(bean, mbd)) {if (mbd.isSingleton()) {
         // Register a DisposableBean implementation that performs all destruction
         // work for the given bean: DestructionAwareBeanPostProcessors,
         // DisposableBean interface, custom destroy method.
         registerDisposableBean(beanName, new DisposableBeanAdapter(
               bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
      }
      else {
         // A bean with a custom scope...
         Scope scope = this.scopes.get(mbd.getScope());
         if (scope == null) {
            throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
         }
         scope.registerDestructionCallback(beanName, newDisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); }}}Copy the code

Code sample

Above a large section of a large section of source code may look a little uncomfortable, it is better to write a demo easy. Since we’re just representing the life cycle, our dependencies are simple, spring-context and spring-beans.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.9. RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.9. RELEASE</version>
</dependency>
Copy the code

We first define a custom post processor, in order to show before and after the initialization and instantiation process, before and after implementation InstantiationAwareBeanPostProcessor interface, Rewrite postProcessBeforeInstantiation ()/postProcessAfterInstantiation ()/postProcessBeforeInitialization ()/postProcessAfterIn Itialization () These four methods.

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
        System.out.println(beanName + "Before instantiation");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "After instantiation");
        return false;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "Pre-initialization");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + "After initialization");
        return null; }}Copy the code

Define a “normal” Bean again, but we also have it implement the InitializingBean, DisposableBean, and BeanNameAware interfaces to reflect the initialization and destruction operations. And implement the corresponding method, Aware interface here I only implement one, just to show the Aware interface injection step.

public class DemoBean implements InitializingBean.DisposableBean.BeanNameAware {

    public DemoBean(a) {
        System.out.println("DemoBean instantiation");
    }

    @Override
    public void afterPropertiesSet(a) throws Exception {
        System.out.println("demoBean afterPropertiesSet");
    }

    public void init(a) {
        System.out.println("demoBean init");
    }

    @Override
    public void destroy(a) throws Exception {
        System.out.println("demoBean destroy");
    }

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

The configuration file is simple, just two beans.


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <beans>
        <bean name="myInstantiationAwareBeanPostProcessor" class="cn.leafw.spring.beans.MyInstantiationAwareBeanPostProcessor" />
        <bean name="demoBean" class="cn.leafw.spring.beans.DemoBean" init-method="init">
        </bean>
    </beans>
</beans>
Copy the code

The test class is also very simple, just start and end.

public class Test {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:application.xml"); applicationContext.registerShutdownHook(); }}Copy the code

Hit Run, and the expected output follows:

Because property injection isn’t easy to test in here, but you know it’s between instantiation and initialization. So in general, the Spring lifecycle is not too difficult to understand, but the key interfaces and classes in the lifecycle need to be remembered, not to mention the ApplicationContext and BeanFactory, but the BeanPostProcessor. Many of the features in Spring are implemented around it. For example, the @AutoWired annotation principle and the Validate data principle, both have corresponding processors.

This blog wrote me a long time, is more write found himself understanding of Spring more shallow, thought devoured the Spring of the document I should be able to easily read the source code, but in fact I still have a difficult, but after understand really refreshing, behind will continue in this pit fill something more, learn more found that not the more, Let’s progress together.