前言
本文配合下面两文食用味道更加
循环依赖是什么?
google上都有这样的答案
但我突然又想到 我们的关注重点是 为什么Spring有这样的问题呢
为什么Sprng会有循环依赖的问题呢?
思考
想知道为什么spring为什么有这样的问题 前提是知道spring是怎么实例化Bean的
Spring是怎么实例化的呢
我们知道Spring实例化(无论单例还是多例)会先进入getBean 接着会进入doGetBean 它的源码重点大致如下(不了解的话 请去看下Spring启动过程——源码分析2)
//先从singletonObjects拿
//拿不到 调用beforeSingletonCreation this.singletonsCurrentlyInCreation.add(beanName)
从this.singletonFactories.get(beanName).getObject()
getSingleton(beanName);
//经过上面的还是拿不到
//同样先从singletonObjects拿 拿不到调用createBean 进入后会进到createBeanInstance
//createBeanInstance会根据实例化方式进行各自的实例化
sharedInstance = getSingleton(beanName, () -> createBean(beanName, mbd, args))
//创建完Bean放入缓存singletonFactories.put(beanName, singletonFactory)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
!! 此时singletonObjects不存在上面的bean
//填充属性
populateBean(beanName, mbd, instanceWrapper);
//初始化Bean 与这里无关
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
复制代码
//这个方法不是很清楚,但我debug发现基本都是直接return bean
getEarlyBeanReference(beanName, mbd, bean)
复制代码
重点getSingleton(beanName)
指向原始对象的引用通过 ObjectFactory 暴露出去 将已经实例化的bean作为参数传到方法里面,循环依赖时等到再次执行getSingleton会执行processor直接返回这个Bean
Bean依赖时执行流程图 StudentA和StudentB相互依赖的情况(从左到右Bean分别是StudentA、StudentB、StudentA)
Spring怎么定义循环依赖
Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池” Set集合singletonsCurrentlyInCreation中,Bean标识符在创建过程中将一直保持 在这个池中,因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖
分析各种注入方式下,会不会出现循环依赖
三种注入方式
属性注入
有参注入
setter注入
根据上边的流程图 那么三种注入方式下,实例化的过程有什么不一样呢? 首先回顾上面的内容(实在太绕了 再看一下)
spring单例下真正实例化Bean的地方
getSingleton(beanName, singletonFactory)
复制代码
大致执行如下
Object singletonObject = this.singletonObjects.get(beanName);
if(singletonObject!=null) return;
//singletonsCurrentlyInCreation.add(name) 解决循环依赖
beforeSingletonCreation(beanName);
//真正实例化bean 其实也就是createBean这个方法 //最终会进入到无参或者有参的实例化
singletonObject = singletonFactory.getObject();
//singletonsCurrentlyInCreation.remove(name) 解决循环依赖
afterSingletonCreation(beanName);
addSingleton(beanName, singletonObject);//加入缓存
return singleObject
复制代码
spring多例下真正实例化Bean的地方
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//this.prototypesCurrentlyInCreation.set(beanName)
beforePrototypeCreation(beanName);
//最终会进入到无参或者有参的实例化
prototypeInstance = createBean(beanName, mbd, args)
//this.prototypesCurrentlyInCreation.remove();
afterPrototypeCreation(beanName)
复制代码
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,
"Requested bean is currently in creation:
Is there an unresolvable circular reference?");
}
复制代码
以StudentA->StudentB->StudentA->为例
1. 单例情况下属性注入和setter注入
首先StudentA调用getBean进入createBeanInstance这个方法里,由于是无参实例化 会直接用反射返回一个对象bean,再调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))暴露ObjectFactory,接着进入populateBean,在这里属性还是setter注入的区别就是通过不同的方式去对StudentB赋值,一样的是都要去getBean("StudentB")->getSingleton("StudentB")->getSingleton("StudentB",()->createBean))->createBeanInstance->addSingletonFactory->populateBean->getSingleton("StudentA") 执行到这里 会直接return 刚刚addSingletonFactory时缓存的Bean
此时singletonsCurrentlyInCreation也是有StudentA、B的 只不过没有执行到getSingleton("StudentB",()->createBean)) 所以也就不会报异常
2. 单例情况下有参注入
进入createBeanInstance后
有参构造函数实例化会进入autowireConstructor方法 StudentA会先去获取它参数里的StudentB 此时调用getBean->getSingleton("StudentB",()->createBean))->beforeSingletonCreation("StudentB")->createBeanInstance 再次进入autowireConstructor
StudentB会先去获取它参数里的StudentA 此时调用getBean->getSingleton->beforeSingletonCreation("StudentA")
由于每次singletonsCurrentlyInCreation.add(beanName) 此时singletonsCurrentlyInCreation已经有StudentA
singletonsCurrentlyInCreation.add(beanName)==false -> 抛出BeanCurrentlyInCreationException
3. 多例下属性注入和setter注入
首先StudentA调用getBean进入doGetBean->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentA")->createBean("StudentA", mbd, args)->createBeanInstance->populate->doGetBean("StudentB")->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentB")->createBean("StudentB", mbd, args)->createBeanInstance->populate->doGetBean("StudentA")->isPrototypeCurrentlyInCreation("StudentA")此时返回true 直接抛异常
4. 多例 有参构造注入
首先StudentA调用getBean进入doGetBean 多例直接进入->isPrototypeCurrentlyInCreation("StudentA")->beforePrototypeCreation("StudentA")->createBean("StudentA", mbd, args)->createBeanInstance->autowireConstructor->doGetBean->isPrototypeCurrentlyInCreation("StudentB")->beforePrototypeCreation("StudentB")->createBean("StudentB", mbd, args)->createBeanInstance->autowireConstructor->doGetBean->**isPrototypeCurrentlyInCreation("StudentA")**此时返回true 直接抛异常
和上边差不多
总结
Spring利用递归方式实现Bean的实例化过程,单例下利用singletonsCurrentlyInCreation判断是否是循环依赖,多例下利用prototypesCurrentlyInCreation判断, 开始创建bean会往集合add值,结束时则remove掉,这样的规则,导致了多例下无法解决循环依赖,单例下无法解决构造参数注入的循环依赖
重点是理解整个创建过程