循环依赖图形说明:

循环依赖文字说明:
循坏依赖问题产生的原因是,bean对象的创建实际上是细分为实例化,属性填充,初始化。创建A对象的时候,先实例化A对象,b = null,然后进行属性赋值,为A对象的b属性赋值。此时,由于Spring容器的对象默认是单例的,所以会优先去容器中选择b,如果找到了就直接赋值,没找到再创建。创建B对象的过程与创建A对象类似,在进行属性赋值的时候,去容器中查找a,但是没找到,所以创建A。整个程序进入了一个闭环,这就是我们说的循环依赖。
三级缓存机制,那么到底什么是三级缓存?
从源码中可以看到,一级缓存、二级缓存都是、三级缓存都是Map结构,且一级缓存与三集缓存用的是ConcurrentHashMap<>(),ConcurrentHashMap是一个线程安全的map,这里不做过多解释,有兴趣的可以去看我的这篇文章:JUC并发编程之集合安全问题
singletonObjects存放的是完整的bean对象,从该缓存中取出的bean可以直接使用earlySingletonObjects存放的是不完整的对象,即已实例化但是未初始化的bean对象(不能直接使用),用于解决循坏依赖singletonFactories,单例对象工厂的缓存,存放bean工厂对象,用于解决循环依赖,里面有一些lambda表达式。三级缓存:在createBeanInstance之后,调用addSingletonFactory方法,将对象放入三级缓存

细心的同学可能会发现,在执行完addSingletonFactory.put方法之后,还执行了earlySingletonObjects.remove,也就是把对象从二级缓存中删除,为什么二级缓存中也会有对象?现在为什么又要删掉?这里先留下一个小问题。
二级缓存:第一次从三级缓存中确定对象是代理对象还是普通对象的时候,调用getSingleton,从三级缓存中取出对象并放入二级缓存,同时删除三级缓存

一级缓存:生成完整对象之后,addSingleton,放到一级缓存,同时删除二三级缓存


问:Spring是如何解决循环依赖的?回答关键词:
三级缓存,提前暴露对象,aop
问:如果只有一级缓存行不行?
问:如果只有二级缓存行不行?
aop的实现之后,报错:This means that said other beans do not user final version of the bean(没有使用最终版本的bean对象)问:为什么需要三级缓存?(三级缓存的作用?)
如果一个对象需要被代理,在生成代理对象之前,一定要先生成一个普通的非代理对象(底层源码中生成代理对象的方法要传入一个bean对象)
这也是上面遗留问题的答案,为什么二级缓存中会有对象。移除二级缓存中对象的原因如3所说,要让代理对象覆盖非代理对象。
普通对象和代理对象不能同时出现在容器中,因此当一个对象需要被代理的时候,就要使用代理对象覆盖普通对象。通过getEarlyBeanReference来执行对象的覆盖过程。
只要搞清楚这个方法的具体执行逻辑即可,doCreateBean方法中有这样一段代码
在当前方法中,有可能会用代理对象替换非代理对象,如果没有三级缓存,就无法得到代理对象。换句话说,在整个容器中,包含了同名对象的代理对象和非代理对象,这是不合理的。容器中,对象都是单例的,意味着根据名称只能获取一个对象的值,此时同时存在两个对象的话,使用的时候无法判读取哪一个。谁也无法确认什么时候会调用当前对象,是在其他对象的执行过程中进行调用的,而不是人为指定的。所以必须要保证容器中任何时候都只有一个同名的对象供外部调用, 在三级缓存中,完成了一件事:让代理对象替换非代理对象的工作,确定返回的是唯一的对象
因此所有的bean对象在创建的时候都要优先放在三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象;否则,返回普通对象。
三级缓存是为了解决在aop代理过程中产生的循环依赖问题,如果没有aop的话,二级缓存足以解决循环依赖问题
以上就是我自己对spring循坏依赖与三级缓存的理解,如果有错误的地方,希望大家指正。