Writing in the front

If you fail to answer the question, you will be passed. It’s a fact of life that even if you don’t get passed, you’ll be paid less than your colleagues who know how to solve Spring cycle dependencies. So, today we are going to discuss how to solve the problem of Spring loop dependency.

Pay attention to glacier technology wechat public number, read more technical dry goods articles!!

What are circular dependencies?

Circular dependencies are circular references, in which two or more beans hold each other and form a closed loop. For example, A depends on B, B depends on C, and C depends on A. The diagram below:

Note that this is not a circular call to a function, but an interdependency of objects. A circular call is an infinite loop unless there is a termination condition.

The cycle dependency scenarios in Spring are:

(1) Constructor loop dependency

(2) Cyclic dependence of field attribute

Constructor of circular dependencies problem cannot be solved, can only throw BeanCurrentlyInCreationException abnormalities, when solving attribute circular dependencies, spring is exposed object method in advance.

How do I detect circular dependencies?

Detecting cyclic dependencies is relatively easy. Beans can be marked when they are created, and if recursive calls come back and find that they are being created, they indicate that they are cyclic dependent.

Three kinds of cyclic dependencies

1. Constructor loop dependencies. [This Spring can’t solve it]

The Spring container will place each Bean identifier being created in a currently created Bean pool, where the Bean identifier will remain during creation. So if in the process of creating Bean has found itself in the “current to create large pools of Bean” will be thrown when abnormal BeanCurrentlyInCreationException said circular dependencies; Beans that have been created are cleared from the currently created Bean pool.

The Spring container to create singleton. A, A relies on B, then A placed in the “current to create large pools of Bean”, at this time to create B, rely on C, B and B in the “current to create large pools of Bean”, at this time to create C, C and rely on A, but, at this time A had in the pool, so complains, because the beans in the pool is not initialized, So it depends on an error, (the initialized Bean is removed from the pool).

public class StudentA {
    private StudentB studentB ;
 
    public void setStudentB(StudentB studentB) {
        this.studentB = studentB;
    }

    public StudentA(a) {}public StudentA(StudentB studentB) {
        this.studentB = studentB; }}Copy the code
public class StudentB {
 
    private StudentC studentC ;
 
    public void setStudentC(StudentC studentC) {
        this.studentC = studentC;
    }
    
    public StudentB(a) {}public StudentB(StudentC studentC) {
        this.studentC = studentC; }}Copy the code
public class StudentC {
 
    private StudentA studentA ;
 
    public void setStudentA(StudentA studentA) {
        this.studentA = studentA;
    }
 
    public StudentC(a) {}public StudentC(StudentA studentA) {
        this.studentA = studentA; }}Copy the code

These are the three basic classes, StudentA has the parameter construct StudentB. The parameterized construct for StudentB is StudentC, and the parameterized construct for StudentC is StudentA, which creates a cyclic dependency,

We all hand these three beans over to Spring to manage and instantiate them with the argument construct

<bean id="a" class="com.liuqing.student.StudentA">  
    <constructor-arg index="0" ref="b"></constructor-arg>  
</bean>  
<bean id="b" class="com.liuqing.student.StudentB">  
    <constructor-arg index="0" ref="c"></constructor-arg>  
</bean>  
<bean id="c" class="com.liuqing.student.StudentC">  
    <constructor-arg index="0" ref="a"></constructor-arg>  
</bean>
Copy the code

Here are the test classes:

public class Test {  
    public static void main(String[] args) {  
        ApplicationContext context = new ClassPathXmlApplicationContext("com/liuqing/student/applicationContext.xml");  
        //System.out.println(context.getBean("a", StudentA.class));  }}Copy the code

The following error information is displayed:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:   
    Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Copy the code

2. Setter singleton, default

Diagram of Bean instantiation in Spring

The first two steps in the figure show that Spring instantiates the Bean object and then sets the object properties

Change the configuration file to set injection:

<! --scope="singleton" -->
<bean id="a" class="com.liuqing.student.StudentA" scope="singleton">
    <property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.liuqing.student.StudentB" scope="singleton">
    <property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.liuqing.student.StudentC" scope="singleton">
    <property name="studentA" ref="a"></property>
</bean>
Copy the code

Here are the test classes:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("com/liuqing/student/applicationContext.xml");
        System.out.println(context.getBean("a", StudentA.class)); }}Copy the code

The printed result is:

com.liuqing.student.StudentA@1fbfd6
Copy the code

Spring instantiates the Bean object with a construct, puts the instantiated object into a Map, and provides a way to get a reference to the instantiated object with an unset property. When Spring instantiates StudentA, StudentB, and StudentC, Spring sets the properties of the StudentA, StudentB, and c objects, and StudentA, which relies on StudentB, obtains the singleton from the Map, and so on. There will be no circulation problems

3. Setter mode prototype, prototype

Modify the configuration file to:

<bean id="a" class="com.liuqing.student.StudentA" scope="prototype">
    <property name="studentB" ref="b"></property>
</bean>
<bean id="b" class="com.liuqing.student.StudentB" scope="prototype">
    <property name="studentC" ref="c"></property>
</bean>
<bean id="c" class="com.liuqing.student.StudentC" scope="prototype">
    <property name="studentA" ref="a"></property>
</bean>
Copy the code

Scope =”prototype” means that each request creates an instance object. The difference is that stateful beans both use the Prototype scope, while stateless beans generally use the Singleton singleton scope.

Test cases:

public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("com/liuqing/student/applicationContext.xml");
        // The spring-managed instance must be obtained, since scope="prototype" now instantiates objects only when requested
        System.out.println(context.getBean("a", StudentA.class)); }}Copy the code

Print result:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
    Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
Copy the code

For “prototype-scoped beans, the Spring container cannot do dependency injection because the Spring container does not cache” prototype-scoped beans, so it cannot expose a Bean being created in advance.

How does Spring address loop dependencies

Spring’s theory of cyclic dependency is based on Java reference passing, where properties can be set later when a reference to an object is obtained. (But the constructor must be before the reference is retrieved.)

The initialization of Spring’s singleton is divided into three main steps:

CreateBeanInstance; createBeanInstance; createBeanInstance

(2) populateBean: fill the attribute. This step is mainly to fill the dependent attribute of multiple beans

(3) initializeBean: Call the init method in Spring XML.

As you can see from the initialization of the singleton bean above, loop dependencies occur mainly in the first and second steps, namely constructor loop dependencies and field loop dependencies. For singletons, there is only one object during the lifetime of the Spring container, so it is easy to imagine that this object should be stored in the Cache. Spring uses level 3 caching to solve the problem of singletons’ cyclic dependencies.

These three levels of cache respectively refer to:

  • SingletonFactories: Cache of singleton object factories
  • EarlySingletonObjects: Cache of prematurely exposed singleton objects
  • SingletonObjects: cache of singletonObjects

When creating a bean, the first thing that comes to mind is getting the singleton bean from the cache, which is singletonObjects. If not, and the object is being created, it is retrieved from the secondary cache earlySingletonObjects. If still can’t get and allow singletonFactories by getObject () to obtain, from the three levels of cache singletonFactory. GetObject () (3), if get to: Remove it from singletonFactories and put it into earlySingletonObjects. You’re essentially moving from level 3 cache to level 2 cache.

Spring uses the singletonFactories cache to solve this problem. This cache is of type ObjectFactory. This is the key to resolving the loop dependency, which occurs after createBeanInstance, meaning that the singleton has already been created (the constructor has been called). The object has already been produced, and while it’s not perfect (not yet initialized in steps 2 and 3), it’s already recognizable (it can be located to objects in the heap based on object references), so Spring now exposes the object for everyone to recognize and use.

What’s the good of that? Let’s look at the circular dependency case where A field or setter of A depends on an instance object of B and A field or setter of B depends on an instance object of A. After A completes the first initialization and exposes itself to singletonFactories, A tries to get(B) and finds that B has not yet been created. B finds that it depends on object A when it initializes the first step, so it tries get(A), singletonObjects(definitely not, because A is not fully initialized), earlySingletonObjects (also not), Try singletonFactories at level 3. Because ObjectFactory exposes itself, ObjectFactory. GetObject gets ObjectFactory (although A is not fully initialized, it’s better than nothing). B successfully completed the initialization stages 1, 2 and 3 after getting the object A, and put itself into the level-1 cache singletonObjects after complete initialization. At this point, A is returned to A, and the object that A can hold B has successfully completed its initialization phase 2 and 3. Finally, A has also completed its initialization and is included in the level 1 cache of singletonObjects. Fortunately, since B has obtained the object reference of A, the object that B now holds A has completed its initialization.

With this principle in mind, you can see why Spring can’t solve the problem that A’s constructor relies on instance objects of B while B’s constructor relies on instance objects of A. Because the constructor is executed before the singletonFactories level 3 cache is added, the constructor’s loop dependency cannot be resolved.

conclusion

Instead of using constructor-based dependency injection, you can solve this problem by:

  • Use the @AutoWired annotation on the field and let Spring decide when it’s appropriate to inject
  • Use dependency injection based on setter methods.

Big welfare

Follow the wechat official account of “Glacier Technology” and reply the keyword “Design Mode” in the background to get the PDF document of “23 Design Modes in Simple Java”. Return keyword “Java8” to obtain the Java8 new features tutorial PDF. “Distributed traffic limiting solutions under 100 million levels of traffic” PDF document, the three PDFS are created by the glacial and collated super core tutorial, interview essential!!

Ok, that’s enough for today! Don’t forget to click a like, to see and forward, let more people see, learn together, progress together!!

Write in the last

If you think glacier wrote good, please search and pay attention to “glacier Technology” wechat public number, learn with glacier high concurrency, distributed, micro services, big data, Internet and cloud native technology, “glacier technology” wechat public number updated a large number of technical topics, each technical article is full of dry goods! Many readers have read the articles on the wechat public account of “Glacier Technology” and succeeded in job-hopping to big factories. There are also many readers to achieve a technological leap, become the company’s technical backbone! If you also want to like them to improve their ability to achieve a leap in technical ability, into the big factory, promotion and salary, then pay attention to the “Glacier Technology” wechat public account, update the super core technology every day dry goods, so that you no longer confused about how to improve technical ability!