Spring如何解决循环依赖
三级缓存指什么?
即DefaultSingletonBeanRegistry
中的定义的三个map:
1 | /** Cache of singleton objects: bean name to bean instance. */ |
1. 普通Bean的创建
spring首先会利用反射的方法调用bean的构造函数实例化这个对象,过程如下图:
对象实例化完成,但还没有初始化,将这个对象放到三级缓存:singletonFactores
中:
注意以下addSingletonFactory
这个方法中传入的参数,第一个是字符串类型也就是bean的名字,第二个是一个lambda表达式,相当于传入了一个匿名内部类。完整的方法签名如下图,ObjectFactory
这个接口中只有一个方法getObject
。也就是说当调用getObject
时会调用getEarlyReference
这个方法,参数baen
就是实例化好的bean对象。
注意上面的两个方法,当存在循环依赖时,populateBean
方法中会检测出来,这个下一节详细介绍。
没有循环依赖时,populateBean
这个方法会为对象内属性填充赋值,之后进行初始化。看一下initializeBean
方法内部:
可以看到,在我们指定初始化方法前后会调用bean后置处理器的两个方法。
之后利用addSingleton
方法将对象从三级缓存放到一级缓存中,对象创建完成。
完整的方法调用栈如下:
可以看到当不存在循环依赖时,bean的流向是三级缓存到一级缓存。
2. 当存在循环依赖时
当存在循环依赖,即类A内有B属性,B类内又有A属性时,首先创建A,在populateBean
方法内检测到需要B类,那么就会重复上述创建过程,实例化B之后将B放到三级缓存,populateBean
时检查到需要A,会将A从三级缓存中移到二级缓存中,同时把A的引用赋给B,B继续执行后面的方法直到创建完成,B从三级缓存移到一级缓存。此时B已经是一个完整的对象了,再把B的引用赋给A,A完成创建,从二级缓存移动到一级缓存中。可以看到Spring主要利用了对象的“中间态”和一个二级缓存解决了循环依赖问题。
注意到存在beanFactory.getBean
的方法,当创建A需要B的时候,就会重复上面创建bean的流程。