06-Solving circular dependency issues in Spirng

Bean circular dependency problem

Circular dependency: A object has B attribute, and B object has A attribute (the husband class Husband has a reference to Wife, and the wife class Wife has a reference to Husband)

When the toString() method is overridden and directly outputs wife/husband, a stack memory overflow error caused by recursion will occur

  • Directly outputting wife/husband will call their toString() method, and the toString() method will call the husband/wife’s toString() method in a loop.
//Husband category
public class Husband {<!-- -->
    private String name;
    private Wife wife;
   // Attribute setter method

    // When the toString() method is rewritten, directly outputting wife will cause a stack memory overflow error caused by recursion, and wife.getName() needs to be output.
    @Override
    public String toString() {<!-- -->
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife.getName() +
                '}';
    }
}
//Wife class
public class Wife {<!-- -->
    private String name;
    private husband;
    //Attribute setter method

    // When the toString() method is rewritten, the husbandband cannot be output directly, and husbandband.getName() needs to be output.
    @Override
    public String toString() {<!-- -->
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband.getName() +
                '}';
    }
}

Set injection under singleton

Spring can solve the circular dependency problem that occurs in the singleton + setter mode, because in this mode Spring’s management of beans is mainly divided into two clear stages

  • Instantiation object phase: When the Spring container instantiates a bean, it will first create the object. Before its attributes are assigned, it will be exposed and added to the cache of the bean being created, so that other beans can directly reference it.
  • Object attribute assignment phase: After Bean exposure, the set method is called to assign attributes.
  • Note: Only when the scope is a singleton, the Bean will take measures to be exposed in advance. It will not be exposed in multiple instances.
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!--singleton represents a singleton, unique object in the entire Spring container-->
    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton">
        <property name="name" value="Zhang San"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
        <property name="name" value="Xiaohua"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>
public class CircularDependencyTest {<!-- -->
    @Test
    public void testSingletonAndSet(){<!-- -->
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Husband husbandBean = applicationContext.getBean("husbandBean", Husband.class);
        Wife wifeBean = applicationContext.getBean("wifeBean", Wife.class);
        //Husband{name="Zhang San",wife=Xiaohua}
        System.out.println(husbandBean);
        //Wife{name="小花",husband=Zhang San}
        System.out.println(wifeBean);
    }
}

Set injection under prototype

If all beans that are circularly dependent have scope="prototype", Spring cannot solve the circular dependency problems caused by them, and BeanCurrentlyInCreationException< will occur. /strong>

  • The Bean is being created exception will only occur when the scope of both beans is prototype. If any one of them is a singleton, no exception will occur.
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <!--Since wifeBean/husbandBean is multi-instance, a new Bean will be created only every time the getBean() method is executed, which will cause an infinite loop-->
    <!--ref is equivalent to manually obtaining the object from the container. Since it is multi-instance, a new object is obtained each time-->
    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype">
        <property name="name" value="Zhang San"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="prototype">
        <property name="name" value="Xiaohua"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
    
     <!--Because wifeBean is a single instance and will be exposed in advance, husbandBean can create new objects-->
    <bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="prototype">
        <property name="name" value="Zhang San"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <!--Exception will only occur when the scope of both beans is prototype. If any one of them is singleton, no exception will occur-->
    <bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
        <property name="name" value="Xiaohua"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
</beans>

Construction injection under singleton

Spring cannot solve the circular dependency caused by structural injection under singleton, and BeanCurrentlyInCreationException will occur

  • Because constructor injection will cause the process of instantiating the object and the process of assigning the object properties to be not separated, resulting in circular dependencies, you must complete the assignment of the object properties if you want to instantiate the object.
//Husband category
public class Husband {<!-- -->
    private String name;
    private Wife wife;

    public Husband(String name, Wife wife) {<!-- -->
        this.name = name;
        this.wife = wife;
    }
    // -----------------------Dividing line----------------------- ----------
    public String getName() {<!-- -->
        return name;
    }

    @Override
    public String toString() {<!-- -->
        return "Husband{" +
                "name='" + name + '\'' +
                ", wife=" + wife +
                '}';
    }
}
//Wife class
public class Wife {<!-- -->
    private String name;
    private husband;

    public Wife(String name, Husband husband) {<!-- -->
        this.name = name;
        this.husband = husband;
    }

    // -------------------------Dividing line--------------------- ----------
    public String getName() {<!-- -->
        return name;
    }

    @Override
    public String toString() {<!-- -->
        return "Wife{" +
                "name='" + name + '\'' +
                ", husband=" + husband +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
    <bean id="hBean" class="com.powernode.spring6.bean2.Husband" scope="singleton">
        <constructor-arg name="name" value="Zhang San"/>
        <constructor-arg name="wife" ref="wBean"/>
    </bean>

    <bean id="wBean" class="com.powernode.spring6.bean2.Wife" scope="singleton">
        <constructor-arg name="name" value="Xiaohua"/>
        <constructor-arg name="husband" ref="hBean"/>
    </bean>
</beans>

Spring’s principle of solving circular dependencies

Spring solves the problem of circular dependencies in the set + singleton mode by separating the two actions of instantiating the Bean and assigning values to the Bean properties (these two steps are not the same. Required to be completed at the same point in time)

  • First instantiate the single-instance Bean: Instantiate all the single-instance Beans by calling the parameterless constructor, and put them into a Map collection (cache) to expose them to the outside world.
  • Then assign values to the Bean properties: Call the setter method to complete the object’s property assignment.

Caching implementation at the underlying source code level of the Spring framework

  • Cache of complete singleton object: key stores the bean name, and the attributes of the singleton Bean object stored in the value have been assigned [Level 1 cache]
  • Caching of early singleton objects: key stores the bean name, and value stores the early Bean object attributes that are not assigned [secondary cache]
  • Cache of singleton factory objects: key stores the bean name, and value stores the ObjectFactory singleton factory object corresponding to each Bean object [Level 3 cache]

Complete Bean creation and attribute assignment in the doCreateBean() method of the AbstractAutowireCapableBeanFactory class

  • creatBeanInstance will only create a Bean object. The addSingletonFactory method caches the bean object and then exposes it. The populateBean method fills the bean and assigns values to the properties of the bean.

  • The function of the addSingletonFactory() method of DefaultSingletonBeanRegistry is to expose the ObjectFactory factory object that creates the Bean object in advance.

Spring will first obtain the Bean from the Level 1 cache. If it cannot obtain it, it will obtain the Bean from the Level 2 cache. If it still cannot obtain it, it will obtain the Bean from the Level 3 cache. to obtain the previously exposed ObjectFactory object, and then obtain the Bean instance through the ObjectFactory object and put it in the second-level cache

syntaxbug.com © 2021 All Rights Reserved.