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

前言

本文配合下面两文食用味道更加

Spring启动过程——源码分析1

Spring启动过程——源码分析2

循环依赖是什么?

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)

重点 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
指向原始对象的引用通过 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掉,这样的规则,导致了多例下无法解决循环依赖,单例下无法解决构造参数注入的循环依赖
重点是理解整个创建过程

转载于:https://juejin.im/post/5cb6ca48e51d456e88333941

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值