Spring是如何解决循环依赖的?

60 阅读3分钟

一、循环依赖

循环依赖就是指多个Bean之间相互调用对方,形成闭环。可以双向依赖,也可以自我依赖(但是在式设计中应尽量避免),也可以长链依赖,也就是A调用B,B调用C,然后C在调回A。

在Spring中,通过构造器造成的循环依赖,Spring是不可以解决的,因为通过构造器创建的Bean的时候,Bean的属性尚未注入,属于未完成初始化的一个状态。所以在构造器循环依赖的时候,就会卡在第一步,初始化A的时候发现需要B,转而去初始化B,但是发现b需要啊,又会回去初始化A,这样就会造成双方都是未完成初始化的状态,无法成为对方的依赖,导致死循环。

在Spring中,setter注入的循环依赖就可以被Spring解决,因为这样注入就可以先完成实例化,在通过setter进行注入依赖,这样Spring就可以使用三层缓存来暂存未完成初始化的Bean,为需要依赖的临时使用,这样就可以避免死循环。

二、三层缓存

(一)什么是三层缓存

三级缓存也就是Spring通过三个Map来避免循环依赖出现死循环。

一级缓存,主要就是存放已初始化好的Bean

二级缓存,用来存放已经实例化,但是未完成属性注入和初始化的,用于临时存放,在第三级缓存中Bean被调用时,会移到这里。

三级缓存,用来存放Bean的工厂对象,工厂的核心就是在当Bean被AOP增强时,就返回代理对象,否则返回原理的实例。

(二)为什么需要三层缓存

为了解决循环依赖+AOP增强的符合场景。

三级缓存通过工厂的延迟执行特性,确保在真的需要时生成代理对象,而不是提前生成,这样就可以解决循环依赖,又可以保证aop的正确性。

(三)工作流程

以双向循环依赖为例(A→B→A)

1、首先,先创建A,检查一级、二级缓存,没有发现A,开始实例化A,然后再把未初始化的a放到工厂里等待构造一个A的代理对象,存入三级缓存,如果需要被代理,则返回代理对象,否则返回A原来的实例。然后为A注入属性,发现需要注入B,这样A就会先暂停初始化,转而去创建B。

2、检查一级、二级缓存,没有发现B,实例化B,然后为将B放到工厂中,然后开始为B注入属性,发现依赖A,开始检查是否有A,一、二级没有发现,在三级找到创建A的工厂,于是使用工厂开始创建A代理对象,将这个A代理对象放到二级缓存,B将这个A对象注入,完成属性注入和初始化,这样B就完整了,将B存入一级缓存。

3、B已经初始化完成,A继续注入,在一级缓存检查到B,将B注入,A完成初始化,存入一级缓存,将二级缓存中的A移除。