preface

One question we are often asked is how Spring solves the problem of circular dependencies. This question is a high-frequency interview question about Spring, because if you do not deliberately study, I believe that even if read the source code, the interviewer may not be able to suddenly think out of the mystery.

This article mainly aims at this problem, from the point of view of source code to explain its implementation principle.

What is circular dependency

Multiple beans depend on each other to form a closed loop. For example, A depends on B, B depends on C, and C depends on A

In general, if you ask the Spring container how to resolve circular dependencies internally, you’re referring to a scenario in which properties refer to each other in the default singleton Bean. In other words, Spring’s circular dependencies are a problem when injected into the Spring container.

    

How does Spring solve circular dependencies

1. Level 3 caching of singleton beans in Spring

Level 1 cache < also called singleton pool) singletonObjects: Hold Bean objects that have gone through their full life cycle

Level 2 cache: earlySingletonObjects, which hold early exposed Bean objects whose lifecycle is not finished (properties are not fully populated)

Map<String, ObiectFactory<? >> singletonFactories, which holds the factories that can generate beans

2. Life cycle of Beans in Spring

      

3, the main method of Bean initialization

GetSingleton: The bean that you want to get a singleton from the container, if there isn’t one

DoCreateBean: Create the bean without it

PopulateBean: Once created, populate the properties

AddSingleton: After filling, add it to the container for use

4. Specify

A needs B in the creation process, so A puts itself in the level 3 cache to de-instantiate B

When B instantiates it, it sees that it needs A, so B looks in level 1 cache, it doesn’t have one, then in level 2 cache, it doesn’t have one, then in level 3 cache, it finds A and then puts the A from level 3 cache into level 2 cache, and deletes the A from level 3 cache

B initializes, puts itself in the level-1 cache (A is still being created in B), then comes back and creates A, and B is already created, grabs B from level-1 cache, and then creates A, and places A in level-1 cache.

5, graphic

  

Why use level 3 cache

1. Use level 1 cache

Instantiate A-> insert A into singletonObjects-> Insert B into singletonObjects-> Insert B into A properties -> insert B into singletonObjects-> insert B into A properties -> insert B into singletonObjects-> insert B into A properties -> insert B into singletonObjects-> insert B into A properties -> insert The finished product A was put into singletonObjects.

Problem: This basic process works, but if another thread tries to fetch A during the entire process, it might just get A semi-finished A with null properties, which would be A problem.

2. Use level 2 cache

A) use singletonObjects and earlySingletonObjects

Finished products are placed in singletonObjects and semi-finished products in earlySingletonObjects

The process can go like this: Instantiate A -> put the semi-finished A into earlySingletonObjects -> Insert A into EarlysingtonObjects -> Insert A into EarlysingtonObjects -> insert B into singletonObjects and get B from earlyS Delete B from ingletonObjects -> populate B into A property -> place finished A into singletonObjects and delete earlySingletonObjects.

Problem: this process is thread safe, but if you add an AOP aspect to A, it doesn’t work because earlySingletonObjects are all raw objects, and we need to inject proxy objects for A.

B) Use singletonObjects and singletonFactories

The finished products are placed in singletonObjects, and the semi-finished products are captured using singletonFactories

The process goes like this: Instantiate A -> create A’s object factory and put it in singletonFactories -> Instantiate B-> Create B’s object factory and put it in singletonFactories -> Get A’s object factory from singletonFactories and put A in B-> put the finished B in singletonObjects and put it in s Remove B’s object factory with ingletonFactories -> populate B with A’s properties -> put the finished PRODUCT A into singletonObjects and remove A’s object factory.

Question: this process is also applicable to the ordinary IOC, but if A on A plane (AOP), it is also unable to meet demand, because every time by singletonFactories. GetObject () to obtain the proxy object is different.

The last

Recently, many people are in the interview, I have compiled a Java multithreaded data, Spring series bucket (1187 pages of documents), Java systematic data: (including the latest Java core knowledge points in 2021, interview topics and 20 years of summary of the Internet true questions, e-books, etc.), friends in need can follow the public number [program Yuan Xiaowan] can get.