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

先说下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的原始工厂

  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

 

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

Spring如何解决的循环依赖问题_第1张图片

Spring如何解决的循环依赖问题_第2张图片

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

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

你可能感兴趣的:(JAVA)