图解Spring循环依赖,写得太好了
Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题。
其实笔者本人对这类框架源码题还是持一定的怀疑态度的。
如果笔者作为面试官,可能会问一些诸如如果注入的属性为null,你会从哪几个方向去排查这些场景题。
那么既然写了这篇文章,闲话少说,发车看看Spring是如何解决的循环依赖,以及带大家看清循环依赖的本质是什么。正文
通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景。比如几个Bean之间的互相引用:
甚至自己循环依赖自己:
先说明前提:原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。if(isPrototypeCurrentlyInCreation(beanName)){thrownewBeanCurrentlyInCreationException(beanName);}
原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A。。。
这就套娃了,你猜是先StackOverflow还是OutOfMemory?
Spring怕你不好猜,就先抛出了BeanCurrentlyInCreationException
image
基于构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。
那么默认单例的属性注入场景,Spring是如何支持循环依赖的?Spring解决循环依赖
首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。
笔者翻阅Spring文档倒是没有找到三级缓存的概念,可能也是本土为了方便理解的词汇。
在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:singletonObjects它是我们最熟悉的朋友,俗称单例池容器,缓存创建完成单例Bean的地方。singletonFactories映射创建Bean的原始工厂earlySingletonObjects映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为Bean,只是一个Instance。
后两个Map其实是垫脚石级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。
所以笔者前文对三级缓存这个词有些迷惑,可能是因为注释都是以Cacheof开头吧。
为什么成为后两个Map为垫脚石,假设最终放在singletonObjects的Bean是你想要的一杯凉白开。
那么Spring准备了两个杯子,即singletonFactories和earlySingletonObjects来回倒腾几番,把热水晾成凉白开放到singletonObjects中。
闲话不说,都浓缩在图里。
上面的是一张GIF,如果你没看到可能还没加载出来。三秒一帧,不是你电脑卡。
笔者画了17张图简化表述了Spring的主要步骤,GIF上方即是刚才提到的三级缓存,下方展示是主要的几个方法。
当然了,这个地步你肯定要结合Spring源码来看,要不肯定看不懂。
如果你只是想大概了解,或者面试,可以先记住笔者上文提到的三级缓存,以及下文即将要说的本质。循环依赖的本质
上文了解完Spring如何处理循环依赖之后,让我们跳出阅读源码的思维,假设让你实现一个有以下特点的功能,你会怎么做?将指定的一些类实例为单例类中的字段也都实例为单例支持循环依赖
举个例子,假设有类A:publicclassA{privateBb;}类B:publicclassB{privateAa;}
说白了让你模仿Spring:假装A和B是被Component修饰,并且类中的字段假装是Autowired修饰的,处理完放到Map中。其实非常简单,笔者写了一份粗糙的代码,可供参考:放置创建好的beanMapprivatestaticMapString,ObjectcacheMapnewHashMap(2);publicstaticvoidmain(String〔〕args){假装扫描出来的对象Class〔〕classes{A。class,B。class};假装项目初始化实例化所有beanfor(ClassaClass:classes){getBean(aClass);}checkSystem。out。println(getBean(B。class)。getA()getBean(A。class));System。out。println(getBean(A。class)。getB()getBean(B。class));}SneakyThrowsprivatestaticTTgetBean(ClassTbeanClass){本文用类名小写简单代替bean的命名规则StringbeanNamebeanClass。getSimpleName()。toLowerCase();如果已经是一个bean,则直接返回if(cacheMap。containsKey(beanName)){return(T)cacheMap。get(beanName);}将对象本身实例化ObjectobjectbeanClass。getDeclaredConstructor()。newInstance();放入缓存cacheMap。put(beanName,object);把所有字段当成需要注入的bean,创建并注入到当前bean中Field〔〕fieldsobject。getClass()。getDeclaredFields();for(Fieldfield:fields){field。setAccessible(true);获取需要注入字段的classClasslt;?fieldClassfield。getType();StringfieldBeanNamefieldClass。getSimpleName()。toLowerCase();如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可如果缓存没有继续创建field。set(object,cacheMap。containsKey(fieldBeanName)?cacheMap。get(fieldBeanName):getBean(fieldClass));}属性填充完成,返回return(T)object;}
这段代码的效果,其实就是处理了循环依赖,并且处理完成后,cacheMap中放的就是完整的Bean了
这就是循环依赖的本质,而不是Spring如何解决循环依赖。
之所以要举这个例子,是发现一小部分盆友陷入了阅读源码的泥潭,而忘记了问题的本质。
为了看源码而看源码,结果一直看不懂,却忘了本质是什么。如果真看不懂,不如先写出基础版本,逆推Spring为什么要这么实现,可能效果会更好。what?问题的本质居然是twosum!
看完笔者刚才的代码有没有似曾相识?没错,和twosum的解题是类似的。不知道twosum是什么梗的,笔者和你介绍一下:twosum是刷题网站leetcode序号为1的题,也就是大多人的算法入门的第一题。常常被人调侃,有算法面的公司,被面试官钦定了,合的来。那就来一道twosum走走过场。问题内容是:给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引。比如:给定nums〔2,7,11,15〕,target9那么要返回〔0,1〕,因为279这道题的优解是,一次遍历HashMap:classSolution{publicint〔〕twoSum(int〔〕nums,inttarget){MapInteger,IntegermapnewHashMap();for(inti0;inums。length;i){intcomplementtargetnums〔i〕;if(map。containsKey(complement)){returnnewint〔〕{map。get(complement),i};}map。put(nums〔i〕,i);}thrownewIllegalArgumentException(Notwosumsolution);}}
先去Map中找需要的数字,没有就将当前的数字保存在Map中,如果找到需要的数字,则一起返回。
和笔者上面的代码是不是一样?
先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。结尾
如果你是上文笔者提到的陷入阅读源码的泥潭的读者,上文应该可以帮助到你。
可能还有盆友有疑问,为什么一道twosum,Spring处理的如此复杂?
这个想想Spring支持多少功能就知道了,各种实例方式。。各种注入方式。。各种Bean的加载,校验。。各种callback,aop处理等等。。
Spring可不只有依赖注入,同样Java也不仅是Spring。如果我们陷入了某个牛角尖,不妨跳出来看看,可能会更佳清晰哦。
智能手机背后的利益链赚了满钵的供应商,提心吊胆的新技术者手机厂和供应链的那些事。5G、折叠屏、混合变焦相机,一眼望过去,新一轮的技术变革随着MWC2019的开始悄然而起。当用户醉心于这些看似前沿的技术,向着手机厂投向崇拜……
大家都在讲大数据,大数据是什么呢?很高兴能够看到和回答这个问题!如今这个时代,大数据,云计算这些热门概念是人们茶余饭后议论的热点话题,然而很多人还是搞不清楚什么是大数据。今天,每日精彩科技将根据自己的经验……
3直播需要那些设备专业虚拟直播间搭建系列教程首先,我们需要哪些设备才能够搭建出一套专业的个人虚拟直播间呢?第一,直播电脑。我们可以使用笔记本电脑,也可以使用台式电脑,我们更推荐使用的还是电脑。因为台式电脑的接口会比……
她不想再做比尔盖茨的妻子了美国当地时间5月3日,比尔盖茨和妻子梅琳达盖茨在推特上发表联合声明,表示两人将结束长达27年的婚姻。结婚前,比尔盖茨曾在白板上写下结婚的正反论点,分析了婚姻生活的利弊,随……
小众却逆天的5款国产app,资深安卓用户分享作为一个十年安卓用户,使用过的APP不下千款,有很多是图一时新鲜下载的,用过几次就删了,但还是有个别优秀产品例外的。我翻了翻手机,总结了5款适用职场人的APP,好用别忘了……
大数据时代背景下触摸屏技术研究及市场发展之我见【摘要】触摸屏的诞生取代了传统的机械化按键,并且能够与显示屏相连去带来更好的影像效果,是如今最为便捷的输入设备技术,能够大大提升人机交互效果,在手机、平板电脑、教育系统等众多领……
请问你们有谁在拼多多花9。9元领到苹果手机了吗?从来没有想过9。9能买一个苹果手机。拼夕夕的套路最深,不是砍价就是什么百亿补贴计划,不过这个百亿补贴计划我估计就那些抖音上的大网红领过,其他人有多少人领到那个补贴计划的,反正我……
学大数据开发有前途吗?谢谢邀请!首先,当前学习大数据开发是不错的选择,一方面就业岗位相对比较多,另一方面薪资待遇也相对比较高。从研究生的就业情况来看,2019年秋招期间不少大厂都释放出了较多的……
有人说,人的欲望和贪婪才是经济发展的第一动力,对吗?这是一个很好的问题,只是用词上有点欠妥,如果用不知足或许还能勉强。我想人类的发展就是出自不知足的追求,只要你走的是正道,不知足才能使你不断的学习成长,不知足是的发明创造的……
现在手机号码都是实名制,为什么催收的可以用虚拟号?三大运营商自己都有金融公司,所以虚拟号码都出自运营商手,只有平民才会实名制虚拟号其实是网络电话,是一个软件,可以虚拟很多个电话,电话号码跟手机号差不多,话费很便宜,只能打……
问界M5即将大批量交付小康股份加速新能源领域布局本报记者王鹤见习记者冯雨瑶日前,有关小康股份旗下赛力斯与华为合作的AITO问界M5车型,将于3月份实现大批量交付的消息备受关注。2月16日,《证券日报》记者以投资者……
2021。10。07区块链晚讯1。《CF40中国智能金融发展报告2020》:用新加密、区块链等技术提升智能金融监管能力。2。AsyncArt本周推出大型可编程音乐作品MozartBeats。3。……