Spring三级缓存解决循环依赖

有两个Bean对象,A对象中有一个属性的B对象,B对象中有一个属性的A对象,他们都需要依赖注入,但是map单例池中都没有这个对象。正常情况下在进行注入时,如果map单例池中没有需要的Bean对象B就会去创建这个Bean对象B,但如果需要创建的这个Bean对象B又需要依赖注入对象A,这就造成了循环依赖问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wyk4IBXq-1660038093817)(E:\Java笔记\picture\image-20220807101443225.png)]

Spring使用了三级缓存来解决循环依赖。

  • 第一级缓存:单例池 singletonObjects,它用来存放经过完整Bean生命周期过程的单例Bean对象
  • 第二级缓存:earlySingletonObjects,它用来保存哪些没有经过完整Bean生命周期的单例Bean对象,用来保证不完整的bean也是单例
  • 第三级缓存:singletonFactories,它保存的就是一个lambda表达式,它主要的作用就是bean出现循环依赖后,某一个bean到底会不会进行AOP操作

现在有两个Bean,进行循环依赖

@Component
public class AService{
    @Autowired
    BService bService;
    
    public void test(){
        System.out.println(bService);
    }
}
@Component
public class BService{
    @Autowired
    AService aService;
   
}

现在创建Bean的过程如下:

AService 创建的生命周期

  1. 推断构造函数,创建AService的普通对象
  2. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
  3. 填充其他属性
  4. 初始化前操作 @PostConstruct
  5. 初始化 InitialzingBean接口中的afterPropertiesSet()
  6. 初始化后 AOF
  7. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 找不到就创建AService
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

这就造成了循环依赖

可以引入一个map来解决循环依赖问题,在创建AService普通对象的时候,将这个普通对象存入map中,然后在BService创建的生命周期中进行依赖注入时,如果在单例池中没有找到AService对象就去map中找。

AService 创建的生命周期

  1. 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
  2. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
  3. 如果有其他属性就填充其他属性
  4. 初始化前操作 @PostConstruct
  5. 初始化 InitialzingBean接口中的afterPropertiesSet()
  6. 初始化后 AOF
  7. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找,能够找到AService的普通对象,就不用去创建了
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

我们可以发现,到现在为止就使用了两个map,所以明明Spring使用两级缓存就能解决循环依赖为什么最后还要使用三级缓存嘞?肯定是有一些缺陷的。比如,可能AService会进行AOP操作,会创建AServiceProxy代理对象,然后将代理对象放入单例池中,但是BService进行属性赋值 依赖注入的时候是把AService的普通对象进行赋值,所以这里是有问题的,应该是赋值AServiceProxy代理对象。

解决这个问题的思路就是,将AOP的操作提前,如果AService要进行AOP的话,那就就将代理对象放入hushangMap中,而不是放普通对象

AService 创建的生命周期

  1. 推断构造函数,创建AService的普通对象 —> AOP —> hushangMap<beanName, 代理对象>
  2. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
  3. 如果有其他属性就填充其他属性
  4. 初始化前操作 @PostConstruct
  5. 初始化 InitialzingBean接口中的afterPropertiesSet()
  6. 初始化后 AOF
  7. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

现在我们需要在第一步就去判断该对象是否要进行AOP操作,我们不可能所有的Bean在第一步的时候就去进行AOP,因为在Spring中整体的设计是在第5步才会去进行AOP,只有在某种特殊的情况下才需要在第一步去进行AOP。这个特殊情况就是当前Bean出现了循环依赖的情况下才需要提前进行AOP。

AService 创建的生命周期

出现了循环依赖的情况下才需要提前进行AOP。

  1. 推断构造函数,创建AService的普通对象 —> 是否满足循环依赖条件?—> AOP —> hushangMap<beanName, 代理对象>
  2. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
  3. 如果有其他属性就填充其他属性
  4. 初始化前操作 @PostConstruct
  5. 初始化 InitialzingBean接口中的afterPropertiesSet()
  6. 初始化后 AOF
  7. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

我们能够发现,在AService创建中第一步的时候 不太好判断当前Bean是否产生了循环依赖,在BService创建的第二步中才比较好判断是否产生了循环依赖。我们可以再创建AService的时候,将当前创建的Bean的名字存入一个createSet集合中。在BService创建中依赖出入时,如果在单例池中没有找到需要的bean,那就去createSet集合中判断有没有我们需要的Bean,如果有就表示当前需要的Bean对象它自己正在创建中,这也就是循环依赖

AService 创建的生命周期

  1. creatingSet(“AService”)

  2. 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>

  3. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象

  4. 如果有其他属性就填充其他属性

  5. 初始化前操作 @PostConstruct

  6. 初始化 InitialzingBean接口中的afterPropertiesSet()

  7. 初始化后 AOF

  8. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象,如果Bean本来就不需要进行AOP操作这里就还是获取的普通对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

现在还是有问题的,假如现在有三个Bean,进行循环依赖

@Component
public class AService{
    @Autowired
    BService bService;
    
    @Autowired
   	CService cService;
    
    public void test(){
        System.out.println(bService);
    }
}
@Component
public class BService{
    @Autowired
    AService aService;
}
@Component
public class CService{
    @Autowired
    AService aService;
}

那么现在的创建过程如下:

AService 创建的生命周期

  1. creatingSet(“AService”)

  2. 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>

  3. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象

  4. 依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象

  5. 如果有其他属性就填充其他属性

  6. 初始化前操作 @PostConstruct

  7. 初始化 InitialzingBean接口中的afterPropertiesSet()

  8. 初始化后 AOF

  9. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

CService 创建的生命周期

  1. 推断构造函数,创建CService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

可以发现上方创建了两个AServiceProxy代理对象,分别赋值给了BService和CService中的属性,这不符合单例Bean,应该的一个AServiceProxy代理对象放入单例池,并且是这个对象进行依赖注入。

解决这个问题就需要使用第二级缓存 earlySingletonObjects,将创建好的AServiceProxy代理对象放入earlySingletonObjects中,key就是beanName,value就是创建好的代理对象,然后CService就从这里拿。

earlySingletonObjects存放的是还没有经过完整生命周期的单例bean对象。

AService 创建的生命周期

  1. creatingSet(“AService”)

  2. 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>

  3. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象

  4. 依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象

  5. 如果有其他属性就填充其他属性

  6. 初始化前操作 @PostConstruct

  7. 初始化 InitialzingBean接口中的afterPropertiesSet()

  8. 初始化后 AOF

  9. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>没有获取到,就提前AOP 生成AServiceProxy代理对象 —> 存入earlySingletonObjects<AService, AServiceProxy>
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

CService 创建的生命周期

  1. 推断构造函数,创建CService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿,得到AServiceProxy代理对象
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

这里其实是用了三个map,也就是三级缓存,第一个就是单例池singletonObjects用来存放经历了完整Bean生命周期的单例bean,第二个就是earlySingletonObjects存放还没有经过完整Bean生命周期的单例bean,第三个map的用处 就是在BService的第二步中,对AService进行AOP操作是需要AService的普通对象的

Spring里面还是会用到一个map,我们可以暂时理解为前面说的hushangMap,先推断构造函数创建好AService的普通对象后,存入Map中去,key是bean的名字,value中存创建好的bean的名字、普通对象以及beanDefinition,这里有三个对象,value里面如何存放三个对象嘞?Spring是会去定义一个lambda表达式。

AService 创建的生命周期

  1. creatingSet(“AService”),把当前beanName存入一个set集合中
  2. 推断构造函数,创建AService的普通对象 —> singletonFactories<beanName, lambda(beanName,普通对象,beanDefinition)>
  3. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
  4. 如果有其他属性就填充其他属性
  5. 初始化前操作 @PostConstruct
  6. 初始化 InitialzingBean接口中的afterPropertiesSet()
  7. 初始化后 AOF,假如第2步出现循环依赖已经进行了AOP操作,这里就不用再进行一遍了,如下图所示
  8. 如果出现了循环依赖,从二级缓存earlySingletonObjects中取代理对象/普通对象
  9. 放入单例池

BService 创建的生命周期

  1. 推断构造函数,创建BService的普通对象
  2. 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> createSet找,判断是否出现了循环依赖 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>如果没有获取到 ----> 从三级缓存singletonFactories找,得到lambda表达式,并执行lambda表达式,执行的方法中会去判断这Bean是否需要进行AOP—>AOP 生成AServiceProxy代理对象;如果不要进行AOP操作 lambda表达式就会得到一个普通对象 —> 存入earlySingletonObjects
  3. 初始化前操作 @PostConstruct
  4. 初始化 InitialzingBean接口中的afterPropertiesSet()
  5. 初始化后 AOF
  6. 放入单例池

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0AMSPRe1-1660038093819)(E:\Java笔记\picture\image-20220807153808043.png)]

总结:

最终的步骤如下:

AService 创建生命周期

  1. creatintSet(“AService”)
  2. 推断构造函数,实例化得到普通对象 —> singletonFactories(lombda(beanName,普通对象,beanDefinition))
  3. 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找—>没找到 —> 创建BService的Bean对象
  4. 填充其他属性
  5. 初始化前操作,@PostConstruct
  6. 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
  7. 初始化后,AOP操作,判断是否需要
  8. 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
  9. 存入单例池中

BService 创建生命周期

  1. 推断构造函数,实例化得到普通对象
  2. 依赖注入,为aService属性赋值 —> 去singletonObjects中找 —> 没找到 —> creatingSet,判断是否循环依赖 —> 二级缓存earlySingletonObjects中找 —> 没找到 —> singletonFactories --> 代理对象/普通对象 —> 存入二级缓存earlySingletonObjects
  3. 初始化前操作,@PostConstruct
  4. 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
  5. 初始化后,AOP操作,判断是否需要
  6. 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
  7. 存入单例池中
  • 5
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值