如何解决循环依赖

//自己依赖自己
@Service
public class A {
    @Autowired
    private A a;
}
Copy to clipboardErrorCopied


//A和B相互依赖
@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

通过以上代码我们基本就了解了什么是循环依赖 ,当然循环依赖也可以是三个Bean或是多个Bean相互依赖,原理都是一样的,这里我就以两个Bean的相互依赖为例来分析。

两个Bean相互依赖就可能产生问题,如:A依赖B,这时A发现B还没有创建,于是就开始创建B,创建的过程发现B要依赖A,但是A此时还没有创建好,因为A要等B创建好,于是他俩就杠上了。

那如何解决循环依赖呢?

首先明确在 Spring 中创建 Bean 分三步:

  1. 实例化,createBeanInstance,即new 了个对象
  2. 属性注入, set 一些属性值
  3. 初始化,initializeBean,执行一些 aware 接口中的方法,initMethod,AOP代理等

明确了 Spring 创建 Bean 的三步骤之后,我们再来看看它为单例搞的三个 map:

  1. 一级缓存,singletonObjects,存储所有已创建完毕的单例 Bean (完整的 Bean)
  2. 二级缓存,earlySingletonObjects,存储所有仅完成实例化,但还未进行属性注入和初始化的 Bean
  3. 三级缓存,singletonFactories,存储能建立这个 Bean 的一个工厂,通过工厂能获取这个 Bean,延迟化 Bean 的生成,工厂生成的 Bean 会塞入二级缓存

这三个 map 是如何配合的呢?

  1. 首先,获取单例 Bean 的时候会通过 BeanName 先去 singletonObjects(一级缓存) 查找完整的 Bean,如果找到则直接返回,否则进行步骤 2。
  2. 看对应的 Bean 是否在创建中,如果不在直接返回找不到,如果是,则会去 earlySingletonObjects (二级缓存)查找 Bean,如果找到则返回,否则进行步骤 3
  3. 去 singletonFactories (三级缓存)通过 BeanName 查找到对应的工厂,如果存着工厂则通过工厂创建 Bean ,并且放置到 earlySingletonObjects 中。
  4. 如果三个缓存都没找到,则返回 null。

从上面的步骤我们可以得知,如果查询发现 Bean 还未创建,到第二步就直接返回 null,不会继续查二级和三级缓存。

返回 null 之后,说明这个 Bean 还未创建,这个时候会标记这个 Bean 正在创建中,然后再调用 createBean 来创建 Bean,而实际创建是调用方法 doCreateBean。

doCreateBean 这个方法就会执行上面我们说的三步骤:

  1. 实例化
  2. 属性注入
  3. 初始化

在实例化 Bean 之后,会往 singletonFactories 塞入一个工厂,而调用这个工厂的 getObject 方法,就能得到这个 Bean

要注意,此时 Spring 是不知道会不会有循环依赖发生的,但是它不管,反正往 singletonFactories 塞这个工厂,这里就是提前暴露

然后就开始执行属性注入,这个时候 A 发现需要注入 B,所以去 getBean(B),此时又会走一遍上面描述的逻辑,到了 B 的属性注入这一步。

此时 B 调用 getBean(A),这时候一级缓存里面找不到,但是发现 A 正在创建中的,于是去二级缓存找,发现没找到,于是去三级缓存找,然后找到了。

​并且通过上面提前在三级缓存里暴露的工厂得到 A,然后将这个工厂从三级缓存里删除,并将 A 加入到二级缓存中。

然后结果就是 B 属性注入成功。

紧接着 B 调用 initializeBean 初始化,最终返回,此时 B 已经被加到了一级缓存里 。

这时候就回到了 A 的属性注入,此时注入了 B,接着执行初始化,最后 A 也会被加到一级缓存里​,且从二级缓存中删除 A。

Spring 解决依赖循环就是按照上面所述的逻辑来实现的。

重点就是在对象实例化之后,都会在三级缓存里加入一个工厂,提前对外暴露还未完整的 Bean,这样如果被循环依赖了,对方就可以利用这个工厂得到一个不完整的 Bean,破坏了循环的条件。

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值