什么是循环依赖
循环依赖又被成为循环引用,即两个或者多个bean相互之间的持有对方,比如A 引用B,B引用C,C 又引用A,则它们最终反映为一个环,如下图所示:
循环依赖是对象之间的相互依赖关系,循环依赖就是一个死循环,除非有终结条件,否则就是死循环,最终导致内存溢出错误.
解决循环依赖
Spring 容器循环依赖包括构造器循环依赖和setter 循环依赖,prototype范围循环依赖.
构造器循环依赖处理
通过构造器注入构成循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖,
setter循环或者@Autowired进行循环依赖处理
Spring 的循环依赖的理论依据基于java的引用传递,当获得对象的引用时,对象的属性是可以延后设置,但是构造器必须在获取引用之前.
Spring 通过setxxx 或者@Autowired 方法解决循环依赖其实是通过提前暴露一个ObjectFactory对象来完成,即ClassA 在调用构造器完成对象初始化之后,在调用ClassA 的setClassB方法之前就把ClassA 实例化的对象通过ObjectFactory提前暴露到Spring 容器职工.
Spring 容器初始化ClassA通过构造器初始化对象后提前暴露到Spring 容器.
AbstractAutowireCapableBeanFactory.doCreateBean()方法中提前暴露初始化对象的源代码如下:
ClassA 调用setClassB方法,Spring 首先尝试从容器获取ClassB ,此时ClassB 不存在Spring容器中,Spring容器初始化B,同时也会将ClassB提前暴露到Spring容器中,ClassB调⽤setClassA⽅法,Spring从容器中获取ClassA ,因为第⼀步中已经提前暴露了ClassA,因此可以获取到ClassA实例.ClassA通过spring容器获取到ClassB,完成了对象初始化操作。这样ClassA和ClassB都完成了对象初始化操作,解决了循环依赖问题。
prototype范围循环依赖处理
对于原型bean的初始化过程中无论是通过构造器参数循环依赖还是通过setter方法产生循环依赖,Spring 都会直接报错处理.
在AbstractBeanFactory.doGetBean()⽅法中是这样对prototype范围循环依赖进行处理的:
判断是否是prototype循环依赖:
若是prototype循环依赖直接抛出BeanCurrentlyInCreationException异常:
当scope="prototype"时,在获取bean之前如果这个原型bean正在被创建则直接抛出异常,原型bean 在创建之前进行标记这个beanName正在被创建,等创建结束之后会被删除标记.
从源码可以得出这样一个结论: Spring 不支持原型bean的循环依赖.