Spring的核心思想之DI:详解Spring DI循环依赖实现机制
IOC与DI是一个事物不同的两面,在Spring的核心思想之IOC:仿Spring自定义一个实现IOC的容器中对自动注入有了一个初步的了解。Springs中真实的DI是如何实现的呢?一个对象引用另一个对象递归注入属性即可实现后续的实例化,同时如果两个或者两个以上的 Bean 互相持有对⽅(Spring的核心思想之IOC:仿Spring自定义一个实现IOC的容器中就没有实现这个功能),最终形成闭环即所谓的循环依赖又如何实现属性的互相注入呢?
实际Spring的DI切入点也如Spring的核心思想之IOC:仿Spring自定义一个实现IOC的容器中一样使用者第一次调用getBean()方法时,IOC容器出入依赖注入(也是populateBean()方法);还有一种情况是用户配置了非懒加载模式,即让容器在解析注册Bean定义进行预实例化时就触发依赖注入。
Spring bean生命周期具体流程如下:
实际与bean实例化及初始化相关的流程如下(单例模式,其他模式不支持循环依赖):
可以看到三级缓存保存的value其实是一个 Lambda 表达式 ObjectFactory。
在DefaultSingletonBeanRegistry类中提供获得单例bean的方法getSingleton:判断条件中有一条isSingletonCurrentlyInCreation(beanName),就是判断是否循环依赖。
对于Spring循环依赖使用三级缓存实现:一级缓存singletonObjects,存储单例对象bean已经经历了完整生命周期的 Bean;二级缓存earlySingletonObjects(防止循环依赖),存储 singletonObject,这个 Bean 实例化了,还没有初始化完全;三级singletonFactories,存储 singletonFactory——只是一个表达式,使用它之后就会保存结果到二级缓存同时删除此缓存中该表达式。
总结: 1)Spring 不⽀持原型 bean 的循环依赖。
2)单例bean通过setXxx或者@Autowired进⾏循环依赖
Spring 的循环依赖的理论依据基于 Java 的引⽤传递,当获得对象的引⽤时,对象的属性是可以延后设置的,但是构造器必须是在获取引⽤之前。Spring通过setXxx或者@Autowired⽅法解决循环依赖其实是通过提前暴露⼀个ObjectFactory对象来完成的。
现在有个问题是:在populateBean()方法之前为什么使用三级缓存保存一个关于待创建bean的 Lambda 表达式,从上图看直接实例化bean保存在二级缓存中不是更简单明了的解决循环依赖的问题么?这就涉及到Spring的另一大核心思想——AOP。