spring的循环依赖

1、介绍几个概念

官网:Redirecting...

循环依赖

spring循环依赖解决不了构造器注入的
我们AB循环依赖问题只要A的注入方式是setter且singleton,就不会有循环依赖的问题。
默认的单例(singleton)的场景是支持循环依赖的,不报错
原型(prototype)的场景是不支持循环依赖的,会报错(因为spring是依靠三级缓存来解决循环依赖的,而原型是每次都创建一个新的对象,不会保存在缓存中)

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中
DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

   /**
    * Internal marker for a null singleton object:
    * used as marker value for concurrent Maps (which don't support null values).
    */
   protected static final Object NULL_OBJECT = new Object();


   /** Logger available to subclasses */
   protected final Log logger = LogFactory.getLog(getClass());

   /** Cache of singleton objects: bean name --> bean instance */
   // 一级缓存,缓存的是创建的bean实例
   // 单例对象的缓存:bean 名称-bean实例,即所谓的单例池;表示已经经历了完整的生命周期的bean对象
   private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

   /** Cache of singleton factories: bean name --> ObjectFactory */
   // 三级缓存,它缓存的对象是ObjectFetcory,这个是个匿名内部类,
   // 在解决循环依赖很重要,保持前后创建的对象是一致的,代理类或者常规类
   // 单例工厂的高速缓存:bean名称--objectFactory
   // 表示存放生成bean的工厂
   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

   /** Cache of early singleton objects: bean name --> bean instance */
   // 二级缓存,它缓存的是还在创建过程中的对象
   // 早期的单例对象的高速缓存:bean名称 -- bean实例
   // 表示bean的生命周期还没有走完(bean的属性还未填充)就把这个bean存入该缓存中
   // 也就是实例化但未初始化的bean放入该缓存里
   private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
}

循环依赖比较重要的代码

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   // 给缓存里面添加
   addSingletonFactory(beanName, new ObjectFactory<Object>() {
      @Override
      public Object getObject() throws BeansException {
          // 回调
         return getEarlyBeanReference(beanName, mbd, bean);
      }
   });
}


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);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}

下图是B创建完毕是,向一级缓存加数据的截图:

1、a创建过程中需要b,于是a将自己放到三级缓存里面,去实例化b
2、b实例化的时候发现需要a,于是b先查一级缓存,没有,再查询二级缓存,还是没有,再查三级缓存,找到了a,然后三级缓存里面的这个a放到二级缓存里面,并删除三级缓存里面的a
3、b顺利初始化完毕,将自己放到一级缓存里面(此时b里面的a依然事创建中状态),然后回来接着创建a,此时b已经创建结束,直接从一级缓存里面拿到b,然后完成创建,并将a自己放到一级缓存里面

循环依赖:(下面都是我看代码自己的见解)

1、首先是创建对象A,在创建完对象,给属性赋值的时候,由于要给B属性赋值(A还没有进行初始化工作,在赋值之前会把创建的A对象放到三级缓存中,缓存的是一个匿名内部类,这个内部类调用的时机是B给属性赋值的时候)

2、创建对象B,在创建完对象,给属性赋值的时候,由于要给A属性赋值(赋值之前会把创建的B对象放到三级缓存中,回调缓存在三级缓存的A的匿名对象,执行对象的创建过程(可能是代理类哦)),并初始化,保存在一级缓存中

A正在被创建,并赋值

3、给A对象赋值,并初始化,保存在一级缓存中

循环依赖

Spring解决循环依赖依靠的是bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没有初始化但状态--》半成品。
实例化但过程又是通过构造器创建但,如果A还没有创建好出来怎么可能提前暴露,所以构造器但循环依赖无法解决。

假设A、B循环引用,实例化A的时候就将其放入到三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果又AOP处理获取代理后的对象A,注入B,后剩下的流程

  • 1、调用doGetBean方法,想要获取beanA,于是调用getSingleton方法从缓存中查找beanA
  • 2、在getSinglton方法中,从一级缓存中查找,没有,返回null
  • 3、doGetBean方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton的重载方法(参数为objectFactory)
  • 4、在getSingleton方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中,然后回调匿名类部类的createBean方法
  • 5、进入AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否是单例、是否允许提前暴露引用(对于单例的一般为true)、是否正在创建中(即是否在第四部的集合中)。判断为true则将beanA添加到【三级缓存】中
  • 6、对beanA进行属性填充,此时检测beanA依赖于beanB,于是开始查找beanB
  • 7、调用doGetBean方法,和上面的beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
  • 8、此时beanB依赖于beanA,调用getSingleton获取beanA,依此从一级、二级、三级缓存中赵,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取singletonObject,此时这个singletonObject指向的就是上面在doGreateBean方法中实例化的beanA
  • 9、这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
  • 10、随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getSgingleton方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

常问的问题

1、为什么使用构造器注入属性的时候不能解决循环依赖的问题?

原因在于此方式是实例化和初始化分开(在进行实例化的时候不必给属性赋值)来操作,将提前实例化好的对象提前暴露出去,供别人调用,而使用的构造器的时候,必须调用构造器方法了,没有构造器方法无法完成对象实例化操作,也就无法创建对象,那么永远会陷入死循环中。

2、一级缓存能不能解决此问题?

不能,在三个级别的缓存中,放置的对象是有区别的(1、是完成实例化且初始化的对象;2、是实例化但是未初始化的对象;),如果只有一级缓存,就有可能取到实例化但未初始化的对象,属性值都为空,肯定有问题

不能,如果只有一级缓存,那么就意味着完全状态和非完全状态的对象都存在,如果此时需要获取某个对象,恰巧获取到非完全状态的对象,怎么办?(没有办法区分,非完全状态的对象(属性没有赋值完,对象还不能使用))可以添加二级缓存(可以多添加几个缓存来满足)

3、二级缓存能不能解决?为什么使用三级缓存?

理论上来说是可以解决循环依赖的问题,但是要注意了:思考--为什么要在三级缓存中放匿名内部类?

本质在于为了创建代理对象,例如现在有A类,需要生成代理对象,A需不需要实例化,需要

如果在A类上配置AOP,是否需要生成一个代理对象?
我们需要三级缓存的最主要意义在于,你所需要的类有可能是简单对象(实例化、初始化),也可能是需要进行代理的对象,当我们向三级缓存中放置匿名类部类的时候,可以在获取的时候决定到底是简单对象,还是代理对象,这就是三级缓存的意义!!!

在三级缓存中放置的是,生成具体对象的一个匿名内部类,这个匿名内部类可能是代理类,也可能是普通的实例对象,而使用三级缓存就保证了不管是否需要代理对象,都保证了使用的是一个对象,而不会出现,前面使用普通bean,后面使用代理类。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值