Spring学习笔记-循环依赖
IoC循环依赖
什么是循环依赖
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A。

注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结条件。
Spring中循环依赖场景有:
- 构造器的循环依赖(构造器注⼊)
- Field 属性的循环依赖(set注⼊)
- scope为prototype原型的bean循环依赖(⽆法解决):对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依赖,Spring都会直接报错处理。
其中,构造器的循环依赖问题⽆法解决,只能拋出BeanCurrentlyInCreationException异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。
构造器的循环依赖问题⽆法解决的原因是:Bean加载流程需要先实例化,如果构造器就有依赖会实例化失败,创建流程直接就结束了;
解决方案
prototype 原型
通过查看AbstractBeanFactory类中的doGetBean()方法,可以发现源码如下:
1 | protected <T> T doGetBean(final String name, final Class<T> requiredType, |
通过源码可知,在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记这个beanName正在被创建,等创建结束之后会删除标记
Spring 不⽀持原型 bean 的循环依赖。
singleton 单例
单例bean通过setXxx或者@Autowired产生循环依赖时,其实是通过提前暴露⼀个ObjectFactory对象来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
通过Bean加载源码解析中可以知道,Bean创建流程最终到达AbstractAutowireCapableBeanFactory类中doCreateBean()方法中,其中populateBean()方法就是处理属性的自动注入的,相关源码如下:
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { |
属性注入流程的时序图如下所示:
而解决在属性注入时的循环依赖,Spring用到了三级缓存:
- singletonObjects:一级缓存,存放的是完成创建的单例对象;
- earlySingletonObjects:二级缓存,存放的是中间态的对象;
- singletonFactories:三级缓存,存放的是工厂对象;
在doGetBean方法中,可以看到缓存的具体使用逻辑:

其中,getSingleton方法源码如下,其中使用到了三级缓存:

其次,
getObjectForBeanInstance方法主要是判断是否是工厂Bean(FactoryBean)
其次,在创建Bean的时候,会将Bean主动存入缓存中,源码如下:

其中,放入缓存的源码如下:

可以看到放入了三级缓存,且放入的是一个lambda表达式addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));,当获取时调用getObject方法时,其实就是执行了getEarlyBeanReference方法。

由此可见,在解决循环依赖的时候,会把需要执行的后置处理逻辑都执行完,这样在有AOP逻辑时,会在此时生成代理对象。




