spring之Bean的循环依赖问题
创始人
2024-04-30 09:33:30
0

文章目录

  • 一、Bean的循环依赖之Set注入模式下
    • 1、Husband类
    • 2、Wife类
    • 3、Spring配置文件
    • 4、测试类
    • 5、测试结果
    • 6、结论
  • 二、Bean的循环依赖之构造方法注入模式下
    • 1、Husband类
    • 2、Wife类
    • 3、Spring配置文件
    • 4、测试类
    • 5、运行结果
  • 三、Spring解决循环依赖的机理
    • 三级缓存(面试常问)


一、Bean的循环依赖之Set注入模式下

A对象中有B属性,B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
比如:丈夫类Husband、妻子类Wife。Husband类中有Wife类的引用。Wife类中有Husband类的引用。

1、Husband类

husband类中有wife

注意:里边的toString方法对于wife这个属性使用了getName()避免陷入死循环

public class Husband {private String name;private Wife wife;public void setName(String name) {this.name = name;}public void setWife(Wife wife) {this.wife = wife;}public String getName() {return name;}@Overridepublic String toString() {return "Husband{" +"name='" + name + '\'' +", wife=" + wife.getName() +'}';}
}

2、Wife类

wife类中有husband

public class Wife {private String name;private Husband husband;@Overridepublic String toString() {return "Wife{" +"name='" + name + '\'' +", husband=" + husband.getName() +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public void setHusband(Husband husband) {this.husband = husband;}
}

3、Spring配置文件

配置两个bean




4、测试类

    @Testpublic void testDeprndency(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");Husband husband = applicationContext.getBean("husband", Husband.class);System.out.println(husband);Wife wife = applicationContext.getBean("wife", Wife.class);System.out.println(wife);}

5、测试结果

在这里插入图片描述

6、结论

singleotn + setter模式下的循环依赖 spring是没有任何问题的。
singleotn 表示在整个Spring容器当中是单例的,独一无二的。

singleotn + setter模式下的循环依赖 spring是如何应对的?

主要原因是在这种模式下,Spring对Bean的管理主要分为清晰的两个阶段:
第一个阶段:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行“曝光”【不等属性赋值就曝光】
第二个阶段:Bean“曝光”之后,再进行属性的赋值。
核心解决方案是:实例化对象和对象的属性赋值分为两个阶段来完成的。
只有在scope是singleton的情况下,Bean才会采取提前“曝光”的措施

prototy+ setter模式下的循环依赖 spring是会出现异常的

    

在这里插入图片描述
Bean的循环依赖出现问题:BeanCurrentlyInCreationException
注意:当两个Bean的scope都是prototype的时候,才会出现异常,如果其中任意一个是singleton,就不会出现异常

二、Bean的循环依赖之构造方法注入模式下

1、Husband类

去掉set方法,加入构造方法

public class Husband {private String name;private Wife wife;public Husband(String name, Wife wife) {this.name = name;this.wife = wife;}public String getName() {return name;}@Overridepublic String toString() {return "Husband{" +"name='" + name + '\'' +", wife=" + wife.getName() +'}';}
}

2、Wife类

去掉set方法,加入构造方法

public class Wife {private String name;private Husband husband;public Wife(String name, Husband husband) {this.name = name;this.husband = husband;}@Overridepublic String toString() {return "Wife{" +"name='" + name + '\'' +", husband=" + husband.getName() +'}';}public String getName() {return name;}
}

3、Spring配置文件

构造注入,这种循环依赖是否会出现问题?



4、测试类

    @Testpublic void testDeprndency(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");Husband husband = applicationContext.getBean("husband", Husband.class);System.out.println(husband);Wife wife = applicationContext.getBean("wife", Wife.class);System.out.println(wife);}

5、运行结果

在这里插入图片描述
注意:基于构造注入的方式下产生的循环依赖也是无法解决的

Spring只能解决set+singleton模式下的循环依赖。

三、Spring解决循环依赖的机理

根本原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。

  • 实例化Bean的时候:调用无参数构造方法来完成,此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。
  • 给Bean对象属性赋值的时候:调用setter方法来完成。

两个步骤完全是可以分离开去完成的,并且不要求在同一时间点上完成。

也就是说Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(缓存),所有的单例Bean全部实例化之后。我们再慢慢的调用setter方法给属性赋值,这样就解决了循环依赖的问题。

追源码:
双击shift: AbstractAutowireCapableBeanFactory
ctrl + f :doCreateBean 方法

debug:
在这里插入图片描述
继续往下走一步:
这个husband对象的name属性和wife属性是空的,但是这个对象已经创建出来了
在这里插入图片描述
走到下一个断点:
在这里插入图片描述
把这个单例对象缓存起来,具体看看怎么缓存的:

三级缓存(面试常问)

step into进去一个新的类:DefaultSingletonBeanRegistry

在这里插入图片描述

先说说这个类DefaultSingletonBeanRegistry中的三个比较重要的缓存:
private final Map singletonObjects ------ 一级缓存
private final Map earlySingletonObjects ------ 二级缓存
private final Map> singletonFactories ------ 三级缓存

这三个缓存都是Map集合。Map集合的key存储的都是bean的name(bean id)。

一级缓存存储的是:完整的单例Bean对象。也就是说这个缓存中的Bean对象的属性都已经赋值了。是一个完整的Bean对象

二级缓存存储的是:早期的单例Bean对象。这个缓存中的单例Bean对象的属性没有赋值,只是一个早期的实例对象

三级缓存存储的是:单例工厂对象。这个里面存储了大量的“工厂对象”,每一个单例Bean对象都会对应一个单例工厂对象。这个集合中存储的是:创建该单例对象时对应的那个单例工厂对象

继续回到debug:
进来DefaultSingletonBeanRegistry这个类之后addSingletonFactory这个方法执行
在这里插入图片描述
继续执行到:
并没有把Bean对象存进去,是把创建Bean对象的工厂对象存放到map集合。(三级缓存)
往map对象存的这个动作就叫做“曝光”
在这里插入图片描述
“曝光”工厂之后会继续调用getSingleton方法
在这里插入图片描述
然后从一级缓存取对象,拿不到从二级缓存取,拿不到从三级缓存取,
三级缓存取工厂对象,获取这个Bean对象,再把Bean对象放到二级缓存。
在这里插入图片描述
最后执行
populateBean(beanName, mbd, instanceWrapper);才会给属性赋值


相关内容

热门资讯

安卓系统自带的网页,功能与特色... 你有没有发现,每次打开安卓手机,那熟悉的系统界面里总有一个默默无闻的小家伙——安卓系统自带的网页浏览...
美咖云系统安卓版,开启智能生活... 你有没有发现,最近手机上多了一个叫“美咖云系统安卓版”的小家伙?它就像一个魔法师,轻轻一点,就能让你...
安卓系统推荐最好的手机,盘点性... 你有没有想过,拥有一部性能卓越的手机,就像是拥有了移动的宝藏库?在这个信息爆炸的时代,一部好手机不仅...
安卓11系统能精简吗,释放潜能 你有没有发现,随着手机越来越智能,系统也越来越庞大?安卓11系统,这个最新的操作系统,是不是也让你觉...
安卓自动重启系统软件,揭秘原因... 手机突然自动重启,是不是感觉整个人都不好了?别急,今天就来和你聊聊这个让人头疼的安卓自动重启系统软件...
苹果手机x刷安卓系统,探索安卓... 你有没有想过,你的苹果手机X竟然也能刷上安卓系统?是的,你没听错,就是那个一直以来都和我们苹果手机X...
安卓系统智商低吗,智商低下的真... 你有没有想过,为什么安卓系统的智商总被调侃得好像有点低呢?是不是觉得它总是慢吞吞的,有时候还犯点小错...
安卓系统手机联系人,揭秘你的社... 你有没有发现,手机里的联系人列表就像是一个小小的社交圈呢?里面藏着我们的亲朋好友、工作伙伴,甚至还有...
安卓系统免费铃声下载,打造个性... 手机里那首老掉牙的铃声是不是让你觉得有点out了呢?别急,今天就来给你支个招,让你轻松给安卓手机换上...
安卓系统用哪个桌面好,打造个性... 你有没有发现,手机桌面可是我们每天都要面对的“脸面”呢?换一个好看的桌面,心情都能跟着好起来。那么,...
虚拟大师是安卓10系统,功能与... 你知道吗?最近在手机圈里,有个新玩意儿引起了不小的轰动,那就是虚拟大师!而且,更让人惊喜的是,这个虚...
安卓系统与苹果优缺点,系统优缺... 说到手机操作系统,安卓和苹果绝对是两大巨头,它们各有各的特色,就像两道不同的美味佳肴,让人难以抉择。...
安卓win双系统主板,融合与创... 你有没有想过,一台电脑如果既能流畅运行安卓系统,又能轻松驾驭Windows系统,那该有多爽啊?没错,...
安卓系统可精简软件,轻松提升手... 你有没有发现,手机里的安卓系统越来越庞大,软件也越装越多,有时候感觉手机就像个“大肚子”,不仅运行速...
安卓系统基于linux的代码,... 你有没有想过,那个陪伴你每天刷抖音、玩游戏、办公的安卓系统,其实背后有着一套复杂的基于Linux的代...
苹果和安卓的拍照系统,谁更胜一... 你有没有发现,现在手机拍照已经成为我们生活中不可或缺的一部分呢?无论是记录生活的点滴,还是捕捉美丽的...
苹果和安卓系统不同吗,系统差异... 你有没有想过,为什么你的手机里装的是苹果的iOS系统,而朋友的手机却是安卓系统呢?这两种系统,看似都...
安卓系统有多少级,揭秘其多级架... 你有没有想过,那个陪伴我们日常生活的安卓系统,它其实有着丰富的层级结构呢?没错,就是那个让我们的手机...
华为鸿蒙系统与安卓的,技术融合... 你知道吗?最近科技圈可是炸开了锅,华为鸿蒙系统与安卓的较量成为了大家热议的话题。这不,今天我就来给你...
什么安卓手机是苹果系统,搭载苹... 你有没有想过,为什么有些人宁愿花大价钱买苹果手机,而有些人却对安卓手机情有独钟呢?其实,这个问题背后...