• 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @Component
    public class BService{
        @Autowired
        AService aService;
       
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    现在创建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
    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代理对象>
    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
    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

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    @Component
    public class BService{
        @Autowired
        AService aService;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    @Component
    public class CService{
        @Autowired
        AService aService;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    AService 创建的生命周期

    1. creatingSet(“AService”)

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

    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

    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
    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
    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. 存入单例池中
  • 相关阅读:
    @Scheduled实现复杂的corn表达式
    HTML标签
    Python异步编程之web框架 异步vs同步 Redis并发对比
    测试用例评审的旁观记录
    C语言的常量
    Qt包含文件不存在问题解决 QNetworkAccessManager
    设计模式详解(九)——桥接模式
    解题-->在线OJ(十八)
    计算机毕业设计(附源码)python智慧小区团购系统
    jackjson下载地址
  • 原文地址:https://blog.csdn.net/qq_44027353/article/details/126252574