Spring bean life cycle & circular dependency

1, bean lifecycle

Let's review the most basic applications of spring

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean name="source" class="org.example.spring.pojo.Source">
        <property name="drink" value="Latte"/>
        <property name="sugar" value="Add sugar"/>
        <property name="size" value="venti "/>
    </bean>


   
</beans>
public class Source {

    private String drink;
    private String sugar;
    private String size;

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }

    public String getSugar() {
        return sugar;
    }

    public void setSugar(String sugar) {
        this.sugar = sugar;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }
}

public class TestSpring {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Source source = (Source) context.getBean("source");
        System.out.println(source.getDrink());
        System.out.println(source.getSize());
        System.out.println(source.getSugar());
    }
}

Operation results

The above is one of the most basic applications of spring. If we only define the source, we can directly get the source instance and its corresponding method call in the test class TestSpring. Obviously, spring helped us complete the instantiation and initialization of the source class.
We know that the positioning of spring can be understood as a container. The contents of the container are beans. If each bean wants to be used normally, it must complete the steps of instantiation and initialization.

spring instantiates relatively few bean s (reflection). Here we mainly look at the steps of initialization:
After the instantiation operation is completed, the attribute values of the object are still the default values,
Therefore, the first operation of initialization is

Assign a value to the property of the object: populateBean

In the xml file at the beginning of the document, the bean tag can define init method, which is the initialization method.

<bean name="source" class="org.example.spring.pojo.Source" init-method="method">

After the attribute assignment is completed, the method defined by the tag is executed

Execute initialization method: invokeinitialmethods

At this point, there are bean s that can be called completely in the container for us to use

context.getBean("source");

It looks very simple, just two things

  • populateBean
  • invokeInitMethods
    Think about a problem. In addition to defining basic attributes such as sugar size, if we define attributes such as BeanFactory in the object
public class Source {

    private String drink;
    private String sugar;
    private String size;
    
    private BeanFactory beanFactory;
}

How should beanFactory be assigned? We know that the assignment of object attributes can be assigned through the set method. If we add the corresponding setBeanFactory method to beanFactory, can we complete the assignment of beanFactory?
aware, it's time for spring to complete the assignment of BeanFactory like attributes through the tag interface

public interface Aware {

}

Look at the implementation class

There are many subclasses. Find the seemingly familiar BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   /**
    * Callback that supplies the owning factory to a bean instance.
    * <p>Invoked after the population of normal bean properties
    * but before an initialization callback such as
    * {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
    * @param beanFactory owning BeanFactory (never {@code null}).
    * The bean can immediately call methods on the factory.
    * @throws BeansException in case of initialization errors
    * @see BeanInitializationException
    */
   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

Yes, this interface provides a set method. If you want to complete the assignment of BeanFactory property, you can implement this interface. There are many similar interfaces, such as
ApplicationContextAware, ImportAware, ResourceLoaderAware, EnvironmentAware, etc.
Therefore, after completing the populateBean, spring will continue to assign values to the container object properties

Method of calling aware interface: invokeAwareMethods

So far, the operation of attribute assignment has been completed.
Next, normally, the initialization method is called directly, but spring provides us with the bean expansion function, which can also be understood as a hook function. Before and after the initialization method is called, the BeanPostProcessor method will be executed

public interface BeanPostProcessor {

 
   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;


   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

The content between before and after is invokeinitialmethods

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            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 new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }

   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

When invokeinitialmethods is actually executed, it will first judge whether the bean implements the InitializingBean interface. If so, it will first call InitializingBean #afterpropertieset. At this point, the main initialization work of spring is completed. The summary is as follows:

2, Cyclic dependence

What is circular dependency? At what stage?

How does spring solve circular dependency?
Adding a cache is a way to resolve circular dependencies
Specific details:
When the cache is not added, the process of initializing the object is as follows

After adding the cache, the simulated function call stack is as follows:

Keywords: Java Spring

Added by madsosterby on Wed, 26 Jan 2022 04:48:29 +0200