zoukankan      html  css  js  c++  java
  • Spring循环依赖

    什么是循环依赖

    循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

     

    这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,最终会导致内存溢出错误,除非有终结条件。

    Spring中循环依赖场景有:
    (1)构造器的循环依赖
    (2)field属性的循环依赖。

    Spring怎么解决循环依赖

    Spring的循环依赖的理论依据其实是基于Java的引用传递,当我们获取到对象的引用时,对象的field或则属性是可以延后设置的(但是构造器必须是在获取引用之前)。

    Spring的单例对象的初始化主要分为三步:

    (1)createBeanInstance:实例化,其实也就是调用对象的构造方法实例化对象

    (2)populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

    (3)initializeBean:调用spring xml中的init 方法。

    从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二部。也就是构造器循环依赖和field循环依赖。

    那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,Spring为了解决单例的循环依赖问题,使用了三级缓存。

    三级缓存是哪三级?

    首先我们看源码,三级缓存主要指:

     1 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
     2      /** Cache of singleton objects: bean name --> bean instance */ 
     3      //单例bean的cache 一级缓存 存的是Bean(Bean生命周期完成之后,被spring容器管理的对象)
     4     private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
     5      /** Cache of singleton factories: bean name --> ObjectFactory */
     6      //单例对象工厂的cache 二级缓存 存的是工厂 (循环依赖存放,用于Aop,在工厂中对象被创建时可以做其他的事情)
     7     private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
     8      /** Cache of early singleton objects: bean name --> bean instance */ 
     9       //提前暴光的单例对象的Cache 三级缓存 存的是对象(new Object()) (二级缓存工厂创建一个对象之后,存入三级缓存)
    10     private final Map<String, Object> earlySingletonObjects = new HashMap(16);
    11   }

      创建bean的时候,首先是从cache中获取这个单例的bean,这个缓存就是singletonObjects。主要调用方法就就是:

     //DefaultSingletonBeanRegistry
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //从 单例对象中获取bean Object singletonObject
    = this.singletonObjects.get(beanName);
    // 获取到对象为空 并且当前对象正在创建中(new 对象(完成))
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { Map var4 = this.singletonObjects; synchronized(this.singletonObjects) {
    //从earlySingletonObjects中获取 三级缓存 singletonObject
    = this.earlySingletonObjects.get(beanName);
    //获取不到且允许singletonFactories通过getObject()获取,就从二级缓存singletonFactory.getObject()(二级缓存)获取
    if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject();
    //从二级缓存获取,放入到三级缓存,并从二级缓存中移除
    this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

    上面的代码需要解释两个方法:

    isSingletonCurrentlyInCreation()

    判断当前单例bean是否正在创建中,也就是没有初始化完成(比如A的构造器依赖了B对象所以得先去创建B对象, 或则在A的populateBean过程中依赖了B对象,得先去创建B对象,这时的A就是处于创建中的状态。

    allowEarlyReference

    是否允许从singletonFactories中通过getObject拿到对象

    整个过程如下:

     获取到了则:

    this.earlySingletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);

    从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

    如何解决循环依赖的?

    从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个二级cache。这个cache的类型是ObjectFactory,定义如下:

    public interface ObjectFactory<T> {
    T getObject() throws BeansException;
    }

    这个接口在下面被引用

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
            Map var3 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
                }
    
            }
        }

    这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

    如A引用B ,B引用A: 

    1. A对象初始化,提前暴露对象ObjectFactory到 "当前正在创建的bean池->singletonFactories" 三级缓存
    2. A对象开始第二步设置属性,发现自己依赖对象B,尝试singletonObjects.get(B),没有获取到对象B,创建对象B,ObjectFactory并放入到“当前正在创建的bean池” 三级缓存
    3. 对象B开始设置自己的属性,发现依赖了A对象,开始singletonObjects.get(A),没有获取到,因为A对象才创建了第一步,二和三还没有走,尝试从earlySingletonObjects(二级缓存),没有获取到,尝试从三级缓存获取,得到对象(虽然A没有初始化完成,但是能得到对象:对象引用能定位到堆中的对象)(并从三级对象移除,放入到二级对象中此时A在二级缓存中),此时B对象初始化完成,将自己存入一级缓存中
    4. A此时从earlySingletonObjects(二级缓存)得到对象B,完成自己的二,三步 
     

    为什么A的构造函数中不能依赖对象B?

    因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。
      

    为什么要将二级缓存remove并且放入到三级缓存?

       protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
                Map var4 = this.singletonObjects;
                synchronized(this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                              //问题1为什么要从二级缓存删除 放入三级缓存?
                            this.earlySingletonObjects.put(beanName, singletonObject);
                             //问题2 为什么要删除?
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
    
            return singletonObject;
        }
    问题1:解决性能问题:二级缓存的value是一个创建Bean的工厂,工厂创建bean肯定是耗时的,如果当前的Bean被很多地方循环依赖了,那么无需多次通过工厂创建对象,如果当前对象已经
    被工厂创建过了,就从二级缓存移除放入三级缓存,下次再有该bean的获取,就可以直接从三级缓存里面获取了,符合源码中的从三级缓存获取(
      singletonObject = this.earlySingletonObjects.get(beanName);

    问题2:其实三级缓存的最终目的的都是保存同一个对象,所以三个缓存里面不可能都存放一样的对象,既然已经存放到三级缓存了,二级缓存也没有必要了,从map里面remove是为了GC。

    一级缓存(对象Bean)的存放
        protected void addSingleton(String beanName, Object singletonObject) {
            Map var3 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                 //存放一级缓存
                this.singletonObjects.put(beanName, singletonObject);
                //把 二级,三级缓存 当前的对象是删除
                this.singletonFactories.remove(beanName);
                this.earlySingletonObjects.remove(beanName);
                this.registeredSingletons.add(beanName);
            }
        }

    二级缓存(工厂)的存放

      protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(singletonFactory, "Singleton factory must not be null");
            Map var3 = this.singletonObjects;
            synchronized(this.singletonObjects) {
                if (!this.singletonObjects.containsKey(beanName)) {
                    this.singletonFactories.put(beanName, singletonFactory);
                    this.earlySingletonObjects.remove(beanName);
                    this.registeredSingletons.add(beanName);
                }
    
            }
        }
    
    
  • 相关阅读:
    Django的sitemap
    meibu ddns update command
    nginx笔记
    docker里运行cron的要点笔记
    win2008r2 32位odbc安装笔记
    【转载】利用tasker推送手机短信到企业微信(App或者微信公众号)
    关闭树莓派的优化笔记
    自已编译openweb docker image笔记
    【笔记】元学习专题视频(台大·李宏毅)学习记录
    【教程】基于Ubuntu系统的PyTorch虚拟环境配置
  • 原文地址:https://www.cnblogs.com/fanBlog/p/12457704.html
Copyright © 2011-2022 走看看