[Spring source code analysis] The circular dependency of spring bean

Bean's life cycle reference: https://blog.csdn.net/sumengnan/article/details/113702527

 

1. What is the third-level cache?

  1. Level 1 cache (singleton pool): singletionObjects ConcurrentHashMap<beanName,beanobject>
  2. Second level cache (early singleton object): earlySingletionObjects HashMap<beanName, incomplete bean object> (mainly used to solve circular dependency problems)
  3. Three-level cache ( singleton factory): singletonFactories HashMap<beanName,ObjectFactory(lambda expression)> (only used when there is a circular dependency and an aop is required, which is equivalent to doing a preparation work)

By the way, why does the first-level cache use ConcurrentHashMap, but the second and third-level caches use HashMap?

Because the first-level cache stores singleton beans, it must be thread-safe.

The second and third level caches are a pair, and synchronized locks have been added to all the places where they are used to ensure thread safety, so HashMap can be used directly for security. As shown in the figure:

Let me ask what is the difference between singleton bean and singleton mode?

  1. Singleton mode: Indicates that there can only be one object.
  2. Singleton bean: As long as the bean name is different, it doesn't matter if the object is the same.

 

2. Brief description of the life cycle of Bean

The normal bean life cycle is:

  1. Instantiate the bean object.
  2. Fill in the attributes.
  3. Perform other operations (brief description)
  4. Put into the singleton pool (first level cache)

For example:

For example: aService class depends on bService class, bService depends on aService

@Component("aService")
public class AService {
    @Autowired
    private BService bService;

    public BService getBService() {
        System.out.println(bService);
        return bService;
    }
}

=====================
@Component("bService")
public class BService {
    @Autowired
    private AService aService;
    
    public AService getAService() {
        System.out.println(aService);
        return aService;
    }
}

The result is the same whether you initialize aService or bService first. Then deal with aService first

The life cycle of aService at this time is:

     1. Instantiate the aService object (the bottom layer of spring is obtained through reflection).

     2. Fill in the properties of bService -->  Query from the singleton pool (first-level cache) --> Instantiate bService if not found

               Start the life cycle of bService below:

                 2.1. Instantiate the bService object.

                 2.2. Populate the aService object  --> Query from the singleton pool (first level cache) --> instantiate aService if it is not found .

                 2.3. Go to instantiate the aService object again --> keep repeating   (cyclic dependency)

The above is called spring has a circular dependency problem.

How to solve?

Add a middleman (cache). This is the second level cache.

At this point, the life cycle of aService becomes as follows:

     1. Instantiate the aService object  --> put it into the second-level cache Map<aService, aService incomplete object> .

     2. Fill in the bService attributes -->  Query from the singleton pool (first level cache) -->If not found, go to the second level cache to query -->If not found, instantiate bService

               Start the life cycle of bService below:

                 2.1. Instantiate the bService object  --> put it into the second-level cache Map<bService, bService incomplete object> .

                 2.2. Fill in the aService object -->  Query from the singleton pool (first level cache) --> Can  not find go to the second level cache to query --> Found it .

                 2.3. Perform other operations

                 2.4, put the bService object into the singleton pool

     3. Perform other operations

     4. Put the aService object into the singleton pool

At this point, the circular dependency problem has been resolved.

Note: In step 2.2 above, we assigned the incomplete object of aService to the attribute of bService. The above steps seem to have a "problem", but there is no problem in fact.

 

Why does spring still need a three-level cache?

When the bean has a circular dependency and needs to perform aop, the second-level cache will not work.

For example: the life cycle of aService normal aop operation:

  1. Instantiate the bean object.
  2. Fill in the attributes.
  3. Perform other operations (including aop)
  4. Put into the singleton pool (first level cache)

It can be seen that the normal aop operation is carried out in the third step. But bService needs aService attribute, so if there is a circular dependency , you need to perform aop in advance and put it to the second step.

 

How to judge that the bean has a circular dependency problem?

We can add a Set collection to save the beans that are being created. In the second step, it is judged that if the aService that bService depends on is being created, there will be a circular dependency problem. If necessary, perform aop in advance.

 

In the end , the life cycle of the cyclic dependency aService is completely resolved as follows:

     0, singletonsCurrentlyInCreation  Set collection <aService> (representing the bean being created)

     1. Instantiate the aService incomplete object --> put it into the three-level cache <aService, aService original object, beanName, beanDefinition> (the lambda expression programming used at the bottom of spring, use these three information to determine whether to perform aop)

     2. Fill in the bService attributes -->  Query from the singleton pool (first level cache) -->If not found, go to the second level cache to query -->If not found, instantiate bService

               Start the life cycle of bService below:

                 2.0、singletonsCurrentlyInCreation Set集合<bService>

                 2.1. Instantiate and get the incomplete object of bService .

                 2.2. Fill in the aService object  -->  Query from the singleton pool (first level cache) --> If not found, go to the second level cache to query  --> Found that aService is being created --> There is a circular dependency --> Pass the first The third-level cache gets the lambda expression and executes it to get the original object --> if necessary, perform aop to get the proxy object --> put it into the second-level cache .

As shown in the figure:

The post processor InfrastructureAdvisorAutoProxyCreator implementation class is used to perform aop proxy. In its parent class AbstractAutoProxyCreator, there are the following two methods to perform aop in advance and perform aop normally:

Use the map of earlyProxyReferences to determine whether the aop has been performed in advance. If it has been done, the aop will not be performed in the normal step.

Extension question:

Why is the lambda expression executed only once, then removed, and the result is put into the second-level cache?

In order to ensure singleton. Prevent lambda from multiple aop operations.

                 2.3. Perform other operations

                 2.4, put the bService object into the singleton pool

                 2.5、singletonsCurrentlyInCreationSet.remove(bService)

     3. Perform other operations

     4. Put the aService object into the singleton pool

     5、singletonsCurrentlyInCreationSet.remove(aService)

 

3. Why can the @Lazy annotation solve the circular dependency?

Principle:
1. Creation of A: A a=new A();
2. Attribute injection: Found that B is needed, query all the annotations of field b, and find that there is an @lazy annotation, then you will not directly create B, but use dynamic The agent creates an agent class B


3. At this time, A and B are not dependent on each other, and become A dependent on a proxy class B1, and B dependent on A

 

4. How does spring prevent incomplete beans from being read in a multi-threaded environment?

Two locks were added.

1. A lock is added to the entire bean creation process.

2. The other is added to fetch data from the cache

Because the cache must be checked first when getBean is started, and the bean will be created if it is not.

Guess you like

Origin blog.csdn.net/sumengnan/article/details/113778927