Spring 如何解决循环依赖这是一个非常经典的面试问题,那么 Spring 是如何解决循环依赖问题的呢?又是否能够让其解决循环依赖的方法失效呢?
1
JAVA 原生的循环依赖
在 JAVA 原生中遇到循环依赖时可以通过如下步骤解决。
实例化 A 对象
实例化 B 对象
往 A 对象中设置 B 对象
往 B 对象中设置 A 对象
但是有另外一种特殊情况,A 的构造方法参数中包含了 B,B 的构造方法参数中包含了 A,这种情况称为构造方法循环依赖。由于 A 和 B 都需要在实例化对象时提供参数,所以这种循环依赖是无解的。
2
Spring 中的循环依赖
如上所述,Spring 也是无法解决构造方法循环依赖的,但是属性循环依赖在实际使用中我们可以看到 Spring 是可以解决的。
Spring 的解决流程与我们上述的步骤一致:
getBean——取得 A Bean,在 doCreateBean 方法中开始创建 Bean 操作。
createBeanInstance——实例化 A Bean。
populateBean——为 A Bean 设置参数,并调用 getBean 方法创建 B Bean。
== createBeanInstance——实例化 B Bean。
== populateBean——为 B Bean 设置参数,并调用 getBean 方法获得未构造完全的 A Bean。
…
经过以上流程,Spring 就解决了 Bean 的循环依赖,这里面涉及到一个比较关键的方法 getSingleton(String beanName, boolean allowEarlyReference)。
以上代码涉及到了三级缓存的概念,singletonObjects 是第一级缓存,存储了创建好的 Bean;earlySingletonObjects 是第二级缓存,存储了未初始化完成的 Bean;singletonFactories 是第三级缓存,存储了 Bean 工厂。
要解决循环依赖问题,就需要提前暴露未完成属性设值注入的未完成 Bean,否则就无法解决循环依赖。
而从上述代码可知,allowEarlyReference 参数是决定是否提前暴露未完成 Bean 的关键参数。
从上述代码也可知,其实只需要二级缓存 earlySingletonObjects 就可以解决循环依赖问题,并不需要用到第三级缓存。
其实三级缓存是为了解决一个特殊场景的应用的,那就是在 AOP 代理情况下的循环依赖,详细内容在下文继续介绍。
3
循环依赖失效
默认的 Bean 工厂 allowEarlyReference 参数默认是 true,所以可以提前暴露未完成 Bean,进而可以解决循环依赖问题。
如果我们复写这个 Bean 工厂,将 allowEarlyReference 修改为 false,那么 Spring 就无法解决循环依赖问题了。
还有一种更简单的方法,其实 Spring 的 BeanFactory 提供了关闭循环依赖的接口。
将 setAllowCircularReferences 设置为 false 之后,Spring 不会往三级缓存中注入 Bean 工厂,所以也就无法解决循环依赖。
配置方法如下:
以上修改都将导致循环依赖抛出异常:
Error creating bean with name 'twoBean': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'oneBean': Requested bean is currently in creation: Is there an unresolvable circular reference?