Spring loop dependencies are familiar. So today we’re going to look at this question briefly.

What are Spring cycle dependencies

Creating an instance of A relies on B, which in turn relies on an instance of A, forming A closed loop. This is the cyclic dependency in Spring.

Detection and resolution of cyclic dependencies

How many Spring loop dependencies are there?

[bug Mc-10866] – Setters create dependencies [bug Mc-10866] – setters create dependencies [bug Mc-10866] – setters create dependencies [bug Mc-10866] – setters create dependencies

Cycle the dependency detection process

We just learned that the prototype loop dependency and singleton constructor parameter dependency of the Spring loop dependency cannot be solved. It’s impossible to solve, but it’s important to see how Spring detects this. The next step is to analyze the source code. First of all, we have to from refresh# finishBeanFactoryInitialization beginning, because it instantiates all the prototype of the bean. Then call the beanFactory. PreInstantiateSingletons (); Next we introduce the premise to start the real step by step with the source code.

1. Obtain the merged BeanDefinition information first

Next, by judging BD, you can infer whether the current bean is abstract, singleton, or lazy-loaded. If it is not abstract, a singleton but not lazily loaded, proceed further and call the getBean#doGetBean method.

2. Query the object in the cache because the current object has not been initialized

Null is returned because there is no bd information in level 1 cache.

Add MBD (MBD); add MBD (MBD);

Because abstract BD can only be used by inheritance, it cannot be de-instantiated.

4. Load the current MBD dependent DependsOn BeanDefinition first

DependsOn is used to state that the current bean is dependent on another bean. The dependent bean is guaranteed by the container to be instantiated before the current bean is instantiated.

Singleton pattern – Construct parameter dependencies

A5. Try to get it from level 1 cache, but it won’t work

A6. Record the current beanName in CreationSet

getSingleton#beforeSingletonCreation

A7. Start to create instance objects


createBean
    |--> doCreateBean
            |--> createBeanInstance
Copy the code

Get the Constructor Constructor from class and create the object by reflection.

A8. Constructor is found to have an argument of type B

But constructors can have arguments or they can have no arguments, so let’s say arguments first. Just as we described the loop dependency in the beginning, we need to instantiate B before executing the createBeanInstance method.

A9. You need to instantiate B first

We then re-perform the above steps, only this time with type B execution.

B.7, B.8, B.9

throughclassgetConstructorConstructor, carries on the reflection to create objects | - > found a constructor parameter typesA| - > need to instantiateA
Copy the code

Then it goes back [embarrassed] and goes back up. A5. Try to get it from level 1 cache, but it will not be able to get it (1~4 unchanged, say from step 5) The detection of cyclic dependencies has finally changed. Log the current BeanName to CreateSet.. But it turns out that add failed. Insert A (A) into set (A);

Throw a loop dependency exception and the loop dependence-construct parameter dependency of the singleton bean is detected.

The prototype of the circular dependencies Next is the prototype of the circular dependencies detection, we from AbstractBeanFactory. DoGetBean began.

The first four steps of cyclic dependency detection are unchanged, so we start with step 5 directly.

A5 ‘adds the current beanName to threadLocal

As you can see in the above code, there is a beforePrototypeCreation method when entering the else if, This method is to first prototypesCurrentlyInCreation (a threadLocal) to obtain, if not add.

A6 ‘gets the Constructor Constructor via Class and will create the object by reflection

A7 ‘discovers that the constructor has a parameter of type B

A8 ‘needs to instantiate B first

It then goes through the same steps as the previous singleton constructor loop dependency, instantiating B to find A again, to create A. During the creation of A, doGetBean() is called again, and the following code is followed:

else {
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw newBeanCurrentlyInCreationException(beanName); }...}protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return(curVal ! =null &&
            (curVal.equals(beanName) || (curVal instanceofSet && ((Set<? >) curVal).contains(beanName)))); }Copy the code

In entering isPrototypeCurrentlyInCreation approach, will be to determine whether the prototypesCurrentlyInCreation contains A if it returns true. If true is returned, loop dependency exceptions will be thrown!

Loop dependent resolution process

We know that the circular dependencies of singleton beans (setter-generated dependencies) can be resolved. So let’s do a quick analysis: the first four steps are the same as before, and I’ll just start with step five.

5. You can’t get it from level 1 cache.

doGetBean
    |--> getSingleton
            |--> beforeSingletonCreation  // This method is familiar to us.
Copy the code

6. Record the current beanName into prototypesCurrentlyInCreation

By being foresingleton Creation, we put beanName into threadLocal.

Class Cunstructor constructor (Cunstructor)

8. After reflection is created, you get an early object

What are early objects? (Semi-finished product) ① No property injection (no parameter construction) ② no object init method call ③ No post-processor processing but the memory address of the early object and the object after processing is the same, it is the same object.

9. Perform MegerBeanDefinitionPostProcessor operations, such as processing @ Autowire etc

Fun fact, at sign Autowire is setter injection.

10. Encapsulate the early object as an ObjectFactory and put it in the level 3 cache

11. Perform dependency injection populateBean

try {
    // Attribute fill, automatic injection -- general method
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}
Copy the code

GetBean (B) = getBean(B)

The next operation is the same as instantiating A, and B also puts its earlier instance object into the level 3 cache. The property is then injected into populateBean, and it is found that B in turn relies on A to perform the getBean(A) operation.

getBean
    |--> doGetBean
            |--> getSingleton
Copy the code
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    // Go to level 1 cache
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // Go to level 2 cache
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        // Go to level 3 cacheObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
                        if(singletonFactory ! =null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}
Copy the code

Then obtain the combined BD information. Then, like getSingleton, go to the level 1 cache and get it. Check CurrentlyInCreation and find that A is being created; An early instance of A was found in the tertiary cache; Finally, after A is upgraded to level 2 cache and the level 3 cache is cleared, object A is returned. So dependency injection of B is complete, and object B is finally put into level 1 cache, and the second and third levels of information about B are erased.

13. Complete dependency injection

Following step 12 above, injection B is complete.

14. In the end

The dependency injection of A is complete, and object A is finally put into the level 1 cache, and the second and third levels of information about A are removed.

At this point, singleton no-parameter dependency injection is resolved.

The end of the

The whole process of dependency injection, from detection to resolution, is over. Since there are no diagrams, the text descriptions are probably awkward, but I’ll continue to supplement circular dependencies later, as well as deal with a lot of abbreviations. After all, if it’s all too much. That’s why I wrote my first Spring article about circular dependencies, because there’s so much that can be pulled out of them. Thank you for watching. If you have any questions, please discuss with me and make progress together.

Thanks for standing space.bilibili.com/457326371/ liu speak source 】 【 B