Spring5 source code analysis-circular dependency (1)

1. Cyclic dependency
occurs during the bean registration process, when bean A depends on another bean B, bean B depends on bean A
code interpretation:
Insert picture description hereInsert picture description here
this is a cyclic dependency.
If you don't understand the creation of spring beans, please click to view.
So how does the loop occur? When we learned about the creation of the bean, we knew that the initialization of the bean object is done after the instantiation of the bean object is completed, and the property injection (dependency injection) is performed during the initialization process. There is a BService object in the properties of AService, so when injecting properties, BService acts as an early exposure bean (early exposure means that the bean is not in other beans, and it is normally registered one by one).
Circular dependency reference exceptions
When we usually use circular dependencies, we generally don't have circular dependency exceptions. Because the bottom layer has helped us deal with singleton circular dependencies, how do we reproduce this exception?
You should have noticed that the bottom layer is a single case, so we can use multiple cases (prototype mode) to reproduce.
AService.java:

package com.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AService {
    
    

    @Autowired
    BService bService;

}

BService.java:

package com.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BService {
    
    

    @Autowired
    AService aService;
}

Myconfig.java:

package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.service")
public class MyConfig {
    
    
}

Application.java:

package com;

import com.config.MyConfig;
import com.service.AService;
import com.service.BService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
        BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
    }
}

Insert picture description hereClick to open it and AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:274)
you will find that the
Insert picture description hereloop dependency exception thrown by this piece of code is reproduced. So we have no choice but to use multiple cases in the project. How can we solve the circular dependency problem?
it's actually really easy. You can change the code like this:
AService.java:

package com.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("prototype")
public class AService {
    
    

//    @Autowired
    BService bService;

    public void setbService(BService bService) {
    
    
        this.bService = bService;
    }
}

BService.java:

package com.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("prototype")
public class BService {
    
    

//    @Autowired
    AService aService;

    public void setaService(AService aService) {
    
    
        this.aService = aService;
    }
}

Application.java:

package com;

import com.config.MyConfig;
import com.service.AService;
import com.service.BService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Application {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        AService aService = annotationConfigApplicationContext.getBean("AService", AService.class);
        BService bService = annotationConfigApplicationContext.getBean("BService", BService.class);
        aService.setbService(bService);
        bService.setaService(aService);
    }
}

We can manually set the object to solve the problem of circular dependency in many cases. Because in the bottom layer, every time you use ASerivce/BService, it is a different object, so you can't find the exit of the circular dependency, resulting in an infinite loop (of course, the bottom layer does not report an error for a long time in an infinite loop, but throws it through a special judgment. Abnormal)

Understanding the basics
Instantiation: the representative object is just created successfully without assigning values ​​to its attributes.
Initialization: the representative object is created successfully and its attributes are successfully assigned
. Complete object: initialized object.
Baby object: instantiated object
loop Depend on the source code to view
the friends who have known the creation of the previous bean, you should know that the initialization of the bean is to perform the attribute assignment after the object is created, and the appearance of the circular dependency is in the attribute assignment process, so we only show the core part here (cyclic dependency Part)
Let’s start from the protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)beginning.

Insert picture description hereGet beanName in String beanName = transformedBeanName(name);.
Check the getSingleton(beanName); method, this method is to query the cache whether the bean is registered or in the process of registration. Let's take a look at the internal logic of this method

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    
    
        // 一级缓存(singletonObjects)查询bean是否存在
        Object singletonObject = this.singletonObjects.get(beanName);
        // isSingletonCurrentlyInCreation判断是否处于正在创建的途中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    
    
            // 二级缓存(earlySingletonObjects)查询bean是否存在
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
    
    
                synchronized (this.singletonObjects) {
    
    
                    // 一级缓存(singletonObjects)查询bean是否存在
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
    
    
                        // 从二级缓存中取出bean对象
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
    
    
                            // 从三级缓存中取出对象
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
    
    

                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

Then back to the if (isPrototypeCurrentlyInCreation(beanName)) of doGetBean in AbstractBeanFactory we can see where the previous circular reference was abnormal.
After seeing

if (mbd.isSingleton()) {
    
    
					sharedInstance = getSingleton(beanName, () -> {
    
    
						try {
    
    
							return createBean(beanName, mbd, args);
						}

Here is a Lambda expression, which involves createBean, let’s go ahead and see what this expression does

// 一级缓存中查询
Object singletonObject = this.singletonObjects.get(beanName);
// 将此对象添加正在创建过程的bean集合singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
    
    
	// 调用Lambda 表达式中的createBean(beanName, mbd, args)
	singletonObject = singletonFactory.getObject();
	newSingleton = true;
}

Call doCreateBean after entering

Object beanInstance = doCreateBean(beanName, mbdToUse, args);

After entering, there is such a period

if (instanceWrapper == null) {
    
    
	// 通过反射创建bean实例
	instanceWrapper = createBeanInstance(beanName, mbd, args);
}

Go down

// 添加婴儿对象到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    
    
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
    
    
		if (!this.singletonObjects.containsKey(beanName)) {
    
    
			// 添加到三级缓存
			this.singletonFactories.put(beanName, singletonFactory);
			//移除二级缓存中的bean(二级和三级不共存)this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

Then we arrived at the code we are most familiar with

try {
    
    
	// 属性注入
	populateBean(beanName, mbd, instanceWrapper);
	// 代理
	exposedObject = initializeBean(beanName, exposedObject, mbd);
}

Then enter populateBean to

// 属性注入
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    
    
	PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

Because we want to create an object, we finally return to protected T doGetBean, which is used to create BService. Repeat the above steps again until you plan to create an AService again. Until the protected Object getSingleton(String beanName, boolean allowEarlyReference) method has changed.
Insert picture description hereGo directly to the lowest level code, release the AService from the third-level cache and put it into the second-level cache. Because there is the baby object of AService in the third-level cache, that is, the instantiated object AService, so when the BService attribute is injected, The baby object AService is assigned to the properties of BService. Then the initialization of BService is directly completed.
Then BService directly enters this method for secondary processing

if (earlySingletonExposure) {
    
    
	// 对bean进行第二次处理
	Object earlySingletonReference = getSingleton(beanName, false);

Nothing is particularly important afterwards. . .
Finally came to the public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) in DefaultSingletonBeanRegistry

if (newSingleton) {
    
    
	// 将BService添加到一级缓存,删除二级缓存BService
	addSingleton(beanName, singletonObject);
}

Then jump out of all the methods about BService. Continue to call the AService method, and finally
Insert picture description hereinject BService into this set. At this point, the circular dependency has ended.
Insert picture description here
Next, perform the same operation as before, put AService in the first-level cache, and remove the data in the third-level cache. Some people may wonder, it seems that the third-level cache is not needed, and only the first-level and second-level caches can be used to complete the circular dependency operation. This question is placed in terms of circular dependency (2). If you are careful, you will find that I mentioned the second treatment in the previous section. This question will be answered next time.

Guess you like

Origin blog.csdn.net/weixin_43911969/article/details/114391363