转载

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;
复制代码

重点 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

指向原始对象的引用通过 ObjectFactory 暴露出去 将已经实例化的bean作为参数传到方法里面,循环依赖时等到再次执行getSingleton会执行processor直接返回这个Bean

Spring——为什么会有循环依赖(源码)
//这个方法不是很清楚,但我debug发现基本都是直接return bean
getEarlyBeanReference(beanName, mbd, bean)
复制代码
Spring——为什么会有循环依赖(源码)
Spring——为什么会有循环依赖(源码)

Bean依赖时执行流程图 StudentA和StudentB相互依赖的情况(从左到右Bean分别是StudentA、StudentB、StudentA)

Spring——为什么会有循环依赖(源码)

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)
复制代码

afterSingletonCreation 以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 直接抛异常

原文  https://juejin.im/post/5cb6ca48e51d456e88333941
正文到此结束
Loading...