Spring 源码分析之 Spring 三级缓存解决循环依赖问题

1,什么是 Spring 的循环依赖

简单来讲,就是有一个 A 对象,创建 A 的时候发现 A 对象依赖 B,然后去创建 B 对象的时候,又发现 B 对象依赖 C,然后去创建 C 对象的时候,又发现 C 对象依赖 A。这就是所谓的循环依赖。

那么 Spring 在创建 Bean 的时候是如何解决这种关系的依赖呢?(循环依赖)先抛出结论,Spring 使用了三级缓存解决了循环依赖的问题。并且 Spring 能解决哪些循环依赖不能解决哪些循环依赖。以下会详细的阐述。

2,什么是三级缓存

1,第一级缓存:单例缓存池 singletonObjects。

2,第二级缓存:早期提前暴露的对象缓存 earlySingletonObjects。

3,第三级缓存:singletonFactories 单例对象工厂缓存


3,什么是早期暴露的对象

所谓的早提提前暴露的对象就是说,你是一个不完整的对象,你的属性还没有值,你的对象也没有被初始化。这就是早期暴露的对象,只是提前拿出来给你认识认识。但他非常重要。这是多级缓存解决循环依赖问题的一个巧妙的地方。


4,创建 Bean 的整个过程

getSingleton 方法详解

1,getBean 方法肯定不陌生,必经之路,然后调用 doGetBean,进来以后首先会执行 transformedBeanName 找别名,看你的 Bean 上面是否起了别名。然后进行很重要的一步,getSingleton,这段代码就是从你的单例缓存池中获取 Bean 的实例。那么你第一次进来肯定是没有的,缓存里肯定是拿不到的。也就是一级缓存里是没有的。那么它怎么办呢?他会尝试去二级缓存中去拿,但是去二级缓存中拿并不是无条件的,首先要判断 isSingletonCurrentlyInCreation(beanName) 他要看你这个对象是否正在创建当中,如果不是直接就退出该方法,如果是的话,他就会去二级缓存 earlySingletonObjects 里面取,如果没拿到,它还接着判断 allowEarlyReference 这个东西是否为 true。它的意思是说,是否允许让你从单例工厂对象缓存中去拿对象。默认为 true。好了,此时如果进来那么就会通过 singletonFactory.getObject();去单例工厂缓存中去拿。然后将缓存级别提升至二级缓存也就早期暴露的缓存。然后,如果说你第一次都没有从单例缓存中没有获取到对象并且 isSingletonCurrentlyInCreation 也没有被标记。那么直接就返回了后面的流程都不走。

dependsOn

2,getSingleton 执行完以后会走 dependsOn 方法,判断是否有 dependsOn 标记的循环引用,有的话直接卡死,抛出异常。比如说 A 依赖于 B,B 依赖于 A 通过 dependsOn 注解去指定。此时执行到这里就会抛出异常。这里所指并非是构造函数的循环依赖.

beforeSingletonCreation

3,在这里方法里。就把你的对象标记为了早期暴露的对象。提前暴露对象用于创建 Bean 的实例

createBean

4,紧接着就走创建 Bean 的流程开始。在创建 Bean 之前执行了一下 resolveBeforeInstantiation 这个东东。它的意思是说,代理 AOPBean 定义注册信息(也就是那个蛋)但是这里并不是实际去代理你的对象,因为你只是个蛋。你还没有被创建,我给你代理个锤子啊?只是代理了你的 Bean 定义信息,还没有被实例化。把你的 Bean 定义信息放进缓存,以便我想代理真正的目标对象的时候,直接去缓存里去拿。

doCreateBean

5,接下来就真正的走你的创建 Bean 流程

首先走进真正做事儿的方法 doCreateBean 然后找到 createBeanInstance 这个方法,在这里面它将为你创建你的 Bean 实例信息(Bean 的实例)。如果说创建成功了,那么就把你的对象放入缓存中去(将创建好的提前曝光的对象放入 singletonFactories 三级缓存中)将对象从二级缓存中移除因为它已经不是提前暴露的对象了。但是。如果说在 createBeanInstance 这个方法中在创建 Bean 的时候它会去检测你的依赖关系,会去检测你的构造器。然后,如果说它在创建 A 对象的时候,发现了构造器里依赖了 B,然后它又会重新走 getBean 的这个流程,当在走到这里的时候,又发现依赖了 A 此时就会抛出异常。为什么会抛出异常,因为,走 getBean 的时候他会去从你的单例缓存池中去拿,因为你这里的 Bean 还没有被创建好。自然不会被放进缓存中,所以它是在缓存中拿不到 B 对象的。反过来也是拿不到 A 对象的。造成了死循环故此直接抛异常。这就是为什么 Spring IOC 不能解决构造器循环依赖的原因。因为你还没来的急放入缓存你的对象是不存在的。所以不能创建。同理 @Bean 标注的循环依赖方法也是不能解决的,跟这个同理。那么多例就更不能解决了。为什么?因为在走 createBeanInstance 的时候,会判断是否是单例的 Bean 定义信息 mbd.isSingleton();如果是才会进来。所以多例的 Bean 压根就不会走进来,而是走了另一段逻辑,这里不做介绍。至此,构造器循环依赖和 @Bean 的循环依赖还有多例 Bean 的循环依赖为什么不能解决已经解释清楚。然后如果说,Bean 创建成功了。那么会走后面的逻辑


6,将创建好的 Bean 放入缓存

addSingletonFactory。方法就是将你创建好的 Bean 放入三级缓存中。并且移除早期暴露的对象。

populateBean

7,通过 populateBean 给属性赋值

我们知道,创建好的对象,并不是一个完整的对象,里面的属性还没有被赋值。所以这个方法就是为创建好的 Bean 为它的属性赋值。并且调用了我们实现的的 XXXAware 接口进行回调初始化,。然后调用我们实现的 Bean 的后置处理器,给我们最后一次机会去修改 Bean 的属性。其他的地方我不做过多的介绍,我只讲一个重点就是,在 populateBean 里面他会解析你的属性,并且赋值,当发现,A 对象里面依赖了 B,此时又会走 getBean 方法,但这个时候,你去缓存中是可以拿的到的。因为我们在对 createBeanInstance 对象创建完成以后已经放入了缓存当中,所以创建 B 的时候发现依赖 A,直接就从缓存中去拿,此时 B 创建完,A 也创建完,一共执行了 4 次。至此 Bean 的创建完成,最后将创建好的 Bean 放入单例缓存池中 addSingleton();至此 Ioc 创建 Bean 的整个生命周期已经介绍完毕。以及 Spring 是如何通过三级缓存去解决循环依赖的问题。下面附加一张流程脑图。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容