Spring IoC 循环依赖问题
Spring IoC 循环依赖问题
什么是循环依赖
循环依赖其实就是循环引用,也就是两个或者两个以上的 Bean 互相持有对方,最终形成闭环。比如 A 依赖于 B,B 依赖于 C,C 又依赖于 A。
循环调用其实就是一个死循环,除非有终结条件。
Spring 中循环依赖场景有:
- 构造器的循环依赖(构造器注入)
- Field 属性的循环依赖(set注入)
其中,构造器的循环依赖问题无法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决属性循环依赖时,spring 采用的是提前暴露对象的方法。
循环依赖处理机制
- 单例 bean 构造器参数循环依赖(无法解决)
- prototype 原型 bean 循环依赖(无法解决)
对于原型 bean 的初始化过程中不论是通过构造器参数循环依赖还是通过 setXxx 方法产生循环依赖,Spring 都会直接报错处理。
Spring 不支持原型 bean 的循环依赖
进入 AbstractBeanFactory#doGetBean 方法:
在获取 bean 之前如果这个原型 bean 正在被创建则直接抛出异常。原型 bean 在创建之前会标记 beanName 正在被创建,等创建结束之后会删除标记。
单例 bean 通过 setXxx 或者 @Autowired 进行循环依赖
Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前。
Spring 通过 setXxx 或者 @Autowired 方法解决循环依赖其实是通过提前暴露一个 ObjectFactory 对象来完成的。简单来说 ClassA 在调用构造器完成对象初始化之后,在调用 ClassA 的 setClassB 方法之前就把 ClassA 实例化的对象通过 ObjectFactory 提前暴露到 Spring 容器中。
Spring 容器初始化 ClassA 通过构造器初始化对象后提前暴露到 singletonFactories 中。
ClassA 调用 setClassB 方法,Spring 首先尝试从容器中获取 ClassB,此时 ClassB 不存在 Spring 容器中。
Spring 容器初始化 ClassB,同时也会将 ClassB 提前暴露到 singletonFactories 中。ClassB 调用 setClassA 方法,Spring 从 singletonFactories 中获取 ClassA,同时将 ClassA 移到 earlySingletonObjects。至此 ClassB 初始化完毕,并且保存到了单例池中。
随着 ClassB 初始化完毕并返回给 ClassA 的创建过程中,ClassA 也完成了对象初始化操作。