什么是循环依赖?
BeanA 类依赖了 BeanB 类,同时 BeanB 类又依赖了 BeanA 类。这种依赖关系形成了一个闭环,我们把这种依赖关系就称之为循环依赖。
解决循环依赖的步骤:
简单来讲就是因为Spring对Bean的管理大体分为两个阶段吧:第一个阶段:在Spring容器加载的时候,实例化Bean,只要其中任意一个Bean实例化之后,马上进行 “曝光”【属性赋值此时还没赋值,类似于无参构造】第二个阶段:Bean“曝光”之后,就可以再进行属性的赋值(DI)。当然里面具体的实现其实就是依靠着spring的三级缓存机制嘛。
具体步骤
假设A依赖B,B依赖A
step1: A先去一级缓存找,因为没找到,所以要创建一个空壳子、也就是没有DI的对象(类似空构造器的对象)。然后这个时候其实是会把这个半成品放入三级缓存。【曝光】
step2: 然后就准备开始DI了,所以这个时候就要去注入属性B、然后因为从一二三级缓存没找到B,所以就开始去创建B了。然后B也是一样、因为一级缓存没找到,所以就是也创建一个空壳子对象,然后放入三级缓存里。
step3: 这个时候,就要进行DI操作了,然后先去一级缓存找,没找到,然后去二级,没找到。这个时候三级找到了,然后因为三级缓存就是一个bean工厂,然后内部有一个lambda表达式【是为了判断是否进行AOP的一个必要步骤】,之后lambda表达式就会执行了,将三级缓存的A对象【如果是aop就是代理对象】放入二级缓存里。放入之后,把三级的缓存A对象清理掉,注入到B对象里。
step4: 所以这个时候B对象就是个完整对象了,然后B对象就是会放入一级缓存,并且把三级缓存的B清理掉。
step5: 这个时候我们就回到A要DI的步骤,这个时候因为B对象以及在一级缓存了,所以我们可以直接注入了,然后二级缓存的A对象清理掉,然后循环依赖解决了。
注意点:
1.为什么要有三级缓存?
这个主要是是为了解决出现循环依赖时的AOP问题的,不然有二级缓存就够了。因为放入单例池的是代理对象(如果是AOP操作)如果初始化后进行AOP,而我们这个是初始化操作…
2.lambda作用是啥?
判断是否进行AOP操作
3.AOP不都是初始化后操作么,那么这样的话,初始化后的AOP操作不是重复了吗?
初始化后的AOP操作会执行一个方法,那个方法里面有个if,它来判断我们之前是否进行过AOP操作,如果进行了,那么我们就不会再进行AOP操作了。如果没进行,那么就进行AOP操作。
4.所以一级缓存叫做singletonObjects【用于存放完全初始化的单例bean】,二级缓存交earlysingletonObiects【早期的单例对象“因为没有D!,所以是不完整的】,三级缓存叫做singletonFactories【bean工厂,用于解决循环依赖】
追问:一定要三级缓存吗?不能只有二级缓存吗?
不行,主要是为了代理对象 。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是生成具体对象的匿名内部类【lambda表达式】,获取Object的时候,它可以生成代理对象,也可以返回普通对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。
假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,Bean初始 化过程中,通过 BeanPostProcessor 去生成代理对象之后,覆盖掉二级缓存中的普通 Bean对象,那么可能就导致取到的Bean对象不一致了。
最后
如果小伙伴们觉得我写的文章不错的话,那么请给我点点关注,我们下次见!