Spring5源码分析------循环依赖(一)

1.循环依赖
发生在bean的注册过程中,bean A依赖于另一个bean B时,bean B依赖于bean A
代码解释:
在这里插入图片描述在这里插入图片描述
这样就是循环依赖。
不理解spring bean的创建请点击查看
那么是怎么产生循环的呢?我们在学习了bean的创建的时候知道,bean对象的实例化完成之后才是进行初始化,在初始化的过程中进行了属性的注入(依赖注入)。在AService中的属性中有着BService对象,所以在注入属性的时候,BService就作为了早期暴露bean(早期暴露指bean不在其他bean中,正常情况是一个一个bean进行注册)。
循环依赖引用异常
在平常我们使用循环依赖时,一般不会出现循环依赖异常。因为底层已经帮我们进行了单例循环依赖的处理,那么我们要怎么去重现这个异常呢?
应该都注意到了底层是实现的单例,那么我们可以用多例(原型模式)去重现。
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);
    }
}

在这里插入图片描述点开AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:274)
会发现是这一段代码抛出的异常
在这里插入图片描述循环依赖异常就重现了。那么我们在项目中没办法只能用多例,怎么去解决循环依赖问题呢?
其实很简单。这样更改代码即可:
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);
    }
}

我们手动去set对象,就可以解决多例的循环依赖问题。因为在底层中,每使用一次ASerivce/BService他都是不同的对象,所以找不到循环依赖的出口,导致会死循环(当然,底层并不会死循环很久才报错,而是通过特判抛出异常的)

基础知识了解
实例化:代表对象只是创建成功,并未对其属性进行赋值
初始化:代表对象创建成功,并已成功对其属性进行赋值
完整对象:初始化完成的对象
婴儿对象:实例化完成的对象
循环依赖源码查看
了解过前面bean的创建的朋友,应该知道了,bean的初始化是创建对象之后进行属性赋值,而循环依赖的出现正是属性赋值过程中,所以我们这里只进行核心部分展示(循环依赖部分)
我们先从protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)开始看起。

在这里插入图片描述在String beanName = transformedBeanName(name);中获取beanName。
查看getSingleton(beanName);方法,此方法是在缓存中查询此bean是否注册或者在注册的过程中。下面我们查看一下此方法内部逻辑

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;
    }

然后在回到AbstractBeanFactory中doGetBean的if (isPrototypeCurrentlyInCreation(beanName)) 我们可以看到之前的循环引用异常的地方。
之后在看到

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

这里有个Lambda 表达式,里面涉及到了createBean,我们先进去看看这个表达式做了什么

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

进入之后调用doCreateBean

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

进入之后有这么一段

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

再往下走

// 添加婴儿对象到三级缓存
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);
		}
	}
}

然后就到达了我们最熟悉的代码

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

再进入populateBean到

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

因为要创建对象所以最后返回到了protected T doGetBean,用于创建BService。再一次循环走上面的步骤直到再一次打算创建AService。直到到了protected Object getSingleton(String beanName, boolean allowEarlyReference) 方法发生了变化。
在这里插入图片描述直接进入到了最底层代码,将AService从三级缓存释放,放入到二级缓存,因为三级缓存中存在AService的婴儿对象,也就是实例化的对象AService,所以在BService属性注入的时候,将婴儿对象AService赋值给了BService的属性。那么BService的初始化就直接完成了。
那么BService直接进入这个方法,进行二次处理

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

之后没什么特别重要的。。。
最后来到DefaultSingletonBeanRegistry中public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)的

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

之后跳出所有关于BService的方法。继续进行AService方法的调用,最后在
在这里插入图片描述这个set中注入BService。至此,循环依赖已经结束
在这里插入图片描述
接下来进行和之前一样的操作,将AService放在一级缓存,移除三级缓存的数据。可能会有人疑问了,貌似并不需要三级缓存,只用一级和二级缓存即可完成循环依赖操作。这个问题放在循环依赖(二)来讲,细心的会发现我在前面提到了二次处理,这个疑问也下次再解答

猜你喜欢

转载自blog.csdn.net/weixin_43911969/article/details/114391363