spring循环依赖

Spring 循环依赖

什么是循环依赖

概念: 在代码中对象A依赖对象B,对象B依赖对象A,对象相互之间依赖关系;

Class A{
 private B b;
}
Class B{
private A a;
}

Spring能解决什么情况下的依赖

  • Spring单例对象的属性之间形成的循环依赖
Class A {
B b ;
private setB(B b){
this.b=b;
}
}

Spring不能解决循环依赖的情况

  • 原型的依赖,因为已经委托给容器管理
  • 单例的构造器注入;(Spring在refresh()初始化bean对象过程中,需要创建一个Springbean,然后注入其对象)

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

三个缓存对象

1:在DefaultSingletonBeanRegistry 单例的注册中心有三个缓存的对象:
  • singletonObjects 对象的一级缓存,创建完成后的对象,都在该对象中;
  • singletonFactories 单例bean的三级缓存;单例对象工厂的cache,第一次创建的bean单例对象,在第一次创建过程都会现在此缓存一下;
  • earlySingletonObjects 提前曝光的单例对象(对象在进行属性注入的时候,获取到singletonFactories中的对象,会将singletonFactories 中的对象,缓存到earlySingletonObjects中去)
2 源码分析
  • 先分析获取单例对象时候,先从缓存中拿到单例对象
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   //一级缓存
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {

         singletonObject = this.earlySingletonObjects.get(beanName);//首先判断二级缓存是否存在
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//判断三级缓存中是否存在bean对象
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();//从三级缓存中取出数据的时候,需要将bean对象转换成器代理对象,
               this.earlySingletonObjects.put(beanName, singletonObject);//如果三级缓存中存在,吧bean 对象加入到二级缓存中
               this.singletonFactories.remove(beanName);//移除三级缓存中的数据
            }
         }
      }
   }
   return singletonObject;
}

通过上述代码,非常清晰的告诉读者;在单例的对象创建之前,都会先从一级缓存的singletonObjects获取对象;

如果一级缓存中没有,从earlySingletonObjects 提前曝光的二级缓存中获取对象;

  • 在创建bean 开始之前,还会在判断一次缓存中,对象是否存在,从singletonObjects 和 singletonFactories缓存中获取;

    /**
     * Return the (raw) singleton object registered under the given name,
     * creating and registering a new one if none registered yet.
     * @param beanName the name of the bean
     * @param singletonFactory the ObjectFactory to lazily create the singleton
     * with, if necessary
     * @return the registered singleton object
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
       Assert.notNull(beanName, "Bean name must not be null");
       synchronized (this.singletonObjects) {
          //从单例池中取出对象
          Object singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null) {
             //是否正在销毁,异常
             if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                      "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                      "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
             }
             if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
             }
             //验证完 要真正开始创建对象,先从标识该bean正在被创建,因为Springbean创建过程复杂,步骤很多,需要标识
             beforeSingletonCreation(beanName);
             boolean newSingleton = false;
             boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
             if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
             }
             try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
             }
             catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                   throw ex;
                }
             }
             catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                   for (Exception suppressedException : this.suppressedExceptions) {
                      ex.addRelatedCause(suppressedException);
                   }
                }
                throw ex;
             }
             finally {
                if (recordSuppressedExceptions) {
                   this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
             }
             if (newSingleton) {
                addSingleton(beanName, singletonObject);
             }
          }
          return singletonObject;
       }
    }
    

    单例对象在第一次创建的时候,都会将单例对象bean,先放在singletonFactories当中

      /**
       * Add the given singleton factory for building the specified singleton
       * if necessary.
       * <p>To be called for eager registration of singletons, e.g. to be able to
       * resolve circular references.
       * @param beanName the name of the bean
       * @param singletonFactory the factory for the singleton object
       * 
       *
       */
    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
       Assert.notNull(singletonFactory, "Singleton factory must not be null");
       synchronized (this.singletonObjects) {
          if (!this.singletonObjects.containsKey(beanName)) {
             this.singletonFactories.put(beanName, singletonFactory);//加入三级缓存,所以现在singletonFactory,
             this.earlySingletonObjects.remove(beanName);//并从二级缓存中删除
             this.registeredSingletons.add(beanName);
          }
       }
    }
    

总结:根据上述的源代码,getSingleton和addSingletonFactory ,知道了三个缓存中的转换关系;如果A依赖B,B依赖A

  • A对象第一次创建,先执行getSingleton()方法 bean对象会返回为空,在执行addSingletonFactory(),正在创建的bean会先放在singletonFactories 中

  • 因为A依赖B,在处理A的成员变量B的创建过程中,B,也是先执行getSingleton()方法 bean对象会返回为空,在执行addSingletonFactory(),正在创建的bean会先放在singletonFactories 中

  • 所以此时 singletonFactories 中存在两个正在创建的bean,A 和B;

  • 因为B同样依赖A,B的属性依赖A的对象,在创建的时候,先执行getSingleton(),很清楚的发现

    if (singletonFactory != null) {
                   singletonObject = singletonFactory.getObject();//从三级缓存中取出数据的时候,需要将bean对象转换成器代理对象,
                   this.earlySingletonObjects.put(beanName, singletonObject);//如果三级缓存中存在,把bean 对象加入到二级缓存中
                   this.singletonFactories.remove(beanName);//移除三级缓存中的数据
                }
    

    此时对象A从singletonFactory 中取出,并加入到earlySingletonObjects 缓存当中去;并将对象返回;

  • B对象依赖的成员变量A 在创建过程中,在三级缓存中获取得到了对象A,并将其缓存到二级对象earlySingletonObjects中,然后此时回调;B对象先完成创建,并将B对象放在一级缓存(singletonObjects)中

  • B对象,完成initializeBean(aware接口实现,init方法实现,beanpostprocessor实现)完成创建,回溯到A对象中的属性B完成创建

  • A对象的其它属性创建成功,A对象最后从二级缓存中获取到A的对象,放在一级缓存当中

Spring调用栈的时序图

分析spring 解决循环依赖问题,需要主要上面的几个流程,同时把握Spring 源码在初始化Spring bean对象的过程;

  • refresh()方法 abstractApplicationContext的方法,bean对象初始化的入口
  • finishBeanFactoryInitialization() bean 对象创建的入口方法
  • preInstantiateSingletons() ,加载bean对象的RootBeanDefinition,并且开始getBean(),注意bean的动态的代理对象也是再次获取

下面是具体的时序图;有待完善,只记录自己的学习过程


时序图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • 引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行...
    java架构_Zhang阅读 2,101评论 1 3
  • 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式。 通过这种解决方式,我们可以应用在我们...
    java架构_Zhang阅读 349评论 0 0
  • spring针对Bean之间的循环依赖,有自己的处理方案。关键点就是三级缓存。当然这种方案不能解决所有的问题,他只...
    数齐阅读 13,241评论 6 23
  • 循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循...
    余风DR阅读 661评论 0 0
  • 德国哲学家胡塞尔《逻辑研究》 意向性概念涵盖了现象学的全部问题和是现象学最核心的关键词。这是胡塞尔从他的老师布伦塔...
    若木拂埃阅读 1,064评论 0 0