Spring如何解决的循环依赖问题

2020-9-1更新

大白话

对于 setter 注入造成的依赖是通过 Spring 容器提前暴露刚完成构造器注入但未完成其他步骤(如
setter 注入)的 Bean 来完成的,而且只能解决 单例 作用域的 Bean 循环依赖。
1、Spring 容器创建单例“A” Bean,首先检测 singletonFactories 是否包含 A,发现没有,于
是正常创建,然后检测 A 是否包含在 singletonsCurrentlyInCreation 中,没有,则将 A 放入。
将 A 放入到 singletonFactories 中。注入属性时需要 B 实例,于是调用了 getBean(B)方法、
2、Spring 容器创建单例“B” Bean,首先检测 singletonFactories 是否包含 B,发现没有,于
是正常创建,然后检测 B 是否包含在 singletonsCurrentlyInCreation 中,没有,则将 B 放入。
将 B 放入到 singletonFactories 中。注入属性时需要 C 实例,于是调用了 getBean(C)方法、
3、Spring 容器创建单例“C” Bean,首先检测 singletonFactories 是否包含 C,发现没有,于
是正常创建,然后检测 C 是否包含在 singletonsCurrentlyInCreation 中,没有,则将 C 放入。
将 C 放入到 singletonFactories 中。注入属性时需要 A 实例,于是调用了 getBean(A)方法、
4、Spring 容器创建单例“A” Bean,首先检测 singletonFactories 是否包含 A,发现有,于是
返回缓存了的 bean,并将 A 从 singletonFactories 删除,返回 A 实例。
5、C 得到 A 实例。set 进来,B、A 也是这样。结束
 
 
对于 “prototype” 作用域 Bean Spring 容器无法完成依赖注入,因为 “prototype” 作用域的 Bean
Spring 容器不进行缓存,因此无法提前暴露一个创建中的 Bean

先说下spring注入属性为null

最主要的原因就是自己new的,是不给spring分配的,要么改成spring扫描注入,要么自己去获取bean

 

如何理解“依赖”呢,在Spring中有:

  • 构造器循环依赖
  • field属性注入循环依赖

 

构造器循环依赖(失败)

@Service
public class A {  
    public A(B b) {  }
}

@Service
public class B {  
    public B(C c) {  
    }
}

@Service
public class C {  
    public C(A a) {  }
}

field属性注入循环依赖(成功)

@Service
public class A1 {  
    @Autowired  
    private B1 b1;
}

@Service
public class B1 {  
    @Autowired  
    public C1 c1;
}

@Service
public class C1 {  
    @Autowired  public A1 a1;
}

field属性注入循环依赖,多例(失败)

@Service
@Scope("prototype")
public class A1 {  
    @Autowired  
    private B1 b1;
}

@Service
@Scope("prototype")
public class B1 {  
    @Autowired  
    public C1 c1;
}

@Service
@Scope("prototype")
public class C1 {  
    @Autowired  public A1 a1;
}

 

现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。

 

基于构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。

那么默认单例的属性注入场景,Spring是如何支持循环依赖的?

 

Spring解决循环依赖

首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。

在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

  • singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。

  • singletonFactories 映射创建Bean的原始工厂,仅在当前 bean 创建时存在, 是尚未调用 createBean 的 bean。用于 setter 循环依赖时实现注入。

  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance仅在当前 bean 创建时存在,用于检测代理 bean 循环依赖

  • 其实还有一个,singletonsCurrentlyInCreation 是 beanName 的集合,用于检测构造器循环依赖。

 

后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。

一级缓存:
/** 保存所有的singletonBean的实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存:
/** singletonBean的生产工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 
/** 保存所有已经完成初始化的Bean的名字(name) */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
 
/** 标识指定name的Bean对象是否处于创建状态  这个状态非常重要 */
private final Set<String> singletonsCurrentlyInCreation =
    Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

流程大概是

  1. A开始
  2. 到getSingleton
  3. 再到doCreateBean
  4. 然后提前暴露,earlySingletonExposure = true,在singletonFactories中有【0,暴露的A工厂】
  5. A再到populateBean
  6. 填充属性,发现需要属性B
  7. B就开始实例,从getSingleton过来
  8. B到了doCreateBean
  9. B做的跟A一样,提前暴露,earlySingletonExposure = true。所以调用addSingletonFactory,扔到工厂缓存
  10. singletonFactories中有【0,暴露的A工厂】,【1,暴露的B工厂】
  11. B也到populateBean,发现自己需要属性A
  12. 于是A又开始实例,进入getSingleton
  13. 根据集合singletonsCurrentlyInCreation,发现A已经在创建中的状态,于是从A工厂产出“早期”A,挪到“早期”缓存
  14. 利用工厂在earlySingletonObjects创建A【0,早期A】,并移除在singletonFactories的A工厂
  15. 【0,早期A】放入addSingleton,populateBean里的B拿到了早期A
  16. 于是B实例完成,到singletonObjects
  17. B实例完,populateBean里的A也到addSingleton,最后到singletonObjects

 

大白话说就是

A第一次初始化,缓存肯定没有,所以get不到

因为B是通过Autowired关联的,所以初始化A并不会触发B的初始化 

此时初始化结束,但还没设置,所以把A放到三级缓存singletonFactory

这时候要设置值了,也就是populateBean(A),因为A中auttowired了B,所以触发getBean(B),也就是B的初始化

B也是第一次初始化,所以缓存也没有

所以B做了跟A一样的事,初始化,放入三级缓存singletonFactory中

然后给B设置值,B里autowired了A,触发A的初始化

因为A是第二次初始化,直接从三级缓存中singletonFactory拿到了之前放进去的值

拿到缓存中的值,虽然没有初始化完全,但spring认为是可以使用的状态

于是B实例化结束,于是A就拿到B也实例化结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值