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


    一、什么是循环依赖

    循环依赖问题是指:类与类之间的依赖关系形成了闭环,就会导致循环依赖问题的产生。

    例如:A类依赖了B类,B类依赖了C类,而最后C类又依赖了A类,这样就形成了循环依赖问题。

    1. @Component
    2. public class A {
    3. @Autowire
    4. private B b;
    5. }
    6. @Component
    7. public class B {
    8. @Autowire
    9. private C c;
    10. }
    11. @Component
    12. public class C {
    13. @Autowire
    14. private A a;
    15. }

     

    二、Spring的循环依赖有两种场景

    1. 构造器的循环依赖

    2. 属性的循环依赖

    三、Spring的循环依赖如何解决?

    我们都知道,单例Bean初始化完成,要经历三步:

    实例化(Instantiation)→属性赋值(Populate)→初始化(Initialization)

    注入就发生在第二步,属性赋值;对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,结合这个过程,Spring为了解决单例的循环依赖问题,使用了三级缓存。

    Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的

    1. /** Cache of singleton objects: bean name --> bean instance */
    2. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
    3. /** Cache of singleton factories: bean name --> ObjectFactory */
    4. private final Map<String, ObjectFactory>> singletonFactories = new HashMap<String, ObjectFactory>>(16);
    5. /** Cache of early singleton objects: bean name --> bean instance */
    6. private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    这三级缓存分别指:

    1、一级缓存 : Map singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
    2、二级缓存 : Map earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例
    3、三级缓存 : Map> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
     

    获取单例Bean的源码如下:

    1. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    2. ...
    3. @Override
    4. @Nullable
    5. public Object getSingleton(String beanName) {
    6. return getSingleton(beanName, true);
    7. }
    8. @Nullable
    9. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    10. Object singletonObject = this.singletonObjects.get(beanName);
    11. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    12. synchronized (this.singletonObjects) {
    13. singletonObject = this.earlySingletonObjects.get(beanName);
    14. if (singletonObject == null && allowEarlyReference) {
    15. ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
    16. if (singletonFactory != null) {
    17. singletonObject = singletonFactory.getObject();
    18. this.earlySingletonObjects.put(beanName, singletonObject);
    19. this.singletonFactories.remove(beanName);
    20. }
    21. }
    22. }
    23. }
    24. return singletonObject;
    25. }
    26. ...
    27. public boolean isSingletonCurrentlyInCreation(String beanName) {
    28. return this.singletonsCurrentlyInCreation.contains(beanName);
    29. }
    30. protected boolean isActuallyInCreation(String beanName) {
    31. return isSingletonCurrentlyInCreation(beanName);
    32. }
    33. ...
    34. }

    & 拿bean的时候先从一级缓存singletonObjects中获取;
    & 如果获取不到或者对象正在创建中,就从二级缓存earlySingletonObjects中获取;
    & 如果还是获取不到就从三级缓存singletonFactories中获取,然后将获取到的对象放到二级缓存earlySingletonObjects中,并且将bean对应的三级缓存singletonFactories清除;

    & bean初始化完毕,放到一级缓存singletonObjects中,将bean对应的二级缓存earlySingletonObjects清除

    知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。

    四、只使用二级缓存能否解决循环依赖问题?

    不能;

    主要是为了生成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是OK的。但是如果存在代理,三级没有问题,二级就不行了。

    五、如何避免循环依赖问题

    1. 业务代码中尽量不要使用构造器注入

    2. 尽量使用field注入而非setter方法注入

  • 相关阅读:
    GTK进行rgb绘图
    Ubuntu 下载安装包到本地
    初识HashMap
    【Android-Jetpack进阶】8、Paging 分页加载、MVVM 架构
    UE4 调用声音
    JS的装箱和拆箱
    ResFields: 一种即插即用的MLP增容工具
    基于Java+微信小程序实现《电子点餐系统》
    如何使用GPT?初学者的指南
    96. 不同的二叉搜索树
  • 原文地址:https://blog.csdn.net/m0_67942533/article/details/126196287