spring的循环依赖和三级缓存

  1. 什么是循环依赖

    1. A引用了B,而这个时候B也引用了A,那么这种情况实际上就是出现了循环依赖的问题了,实际上也可以把循环依赖称之为循环引用,两个或者两个以上的bean互相持有对方,最终形成闭环
    2. 这里不是函数的循环调用,是对象的相互依赖关系,循环调用其实就是一个死循环,除非有终结条件,否则的话,他就是一个死循环
  2. spring的循环依赖

    1. spring的循环依赖有:
      1. 构造器的循环依赖
      2. field属性的循环依赖
    2. 构造器的循环依赖实际上是个无解的操作,只能抛出BeanCurrentlyInCreationException异常,也就是说,这个构造器导致的循环依赖,spring是没有办法来处理的,也只是给抛出了异常
  3. spring解决循环依赖

    1. spring的单例对象的初始化主要分为三个步骤
      1. createBeanInstance 实例化
      2. populateBean 填充属性
      3. initializeBean 初始化
    2. createBeanInstance实例化实际上就是调用对象的构造方法实例化对象
    3. populateBean实际上就是对bean的依赖属性进行一个赋值填充
    4. initializeBean则是调用spring xml中的int方法
    5. 但从bean的初始化的过程来看,循环依赖发生的位置就是在createBeanInstance实例化,以及populateBean填充属性中
    6. 发生的循环依赖是:构造器的循环依赖和field属性的循环依赖
  4. 三级缓存

    1. singletonObjects:单例对象工厂的cache,用于存放完全初始化好的bean,从该缓存中取出的bean可以直接使用

    2. earlySingletonObjects:提前曝光的单例对象的cache,存放原始的bean对象(尚未填充属性),用于解决循环依赖

    3. singletonFactories:单例对象的cache,存放bean工厂对象,用于解决循环依赖

      1. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一级缓存
        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二级缓存
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三级缓存
        
  5. 对象创建过程

    1. AbstractBeanFactory 中的 doGetBean()方法

      1. protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
        
    2. DefaultSingletonBeanRegistry 中的 getSingleton()方法

      1. protected Object getSingleton(String beanName, boolean allowEarlyReference)
        
      2. 在这个方法中,先从一级缓存singletonObjects中去获取,如果获取到就直接return

      3. 如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取

      4. 如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()获取

      5. 如果获取到了,则从singletonFactories中移除,并放入earlySingletonObjects中

      6. 这相当于把三级缓存中的数据剪贴到了二级缓存中

      7. 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);
             if (singletonFactory != null) {
              singletonObject = singletonFactory.getObject();
              this.earlySingletonObjects.put(beanName, singletonObject);
              this.singletonFactories.remove(beanName);
             }
            }
           }
          }
          return singletonObject;
         }
        
    3. AbstractAutowireCapableBeanFactory 中的 doCreateBean() 方法

      1. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        
         //添加到三级缓存
          if (earlySingletonExposure) {
           if (logger.isTraceEnabled()) {
            logger.trace("Eagerly caching bean '" + beanName +
              "' to allow for resolving potential circular references");
           }
           addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
          }
        
    4. AbstractAutowireCapableBeanFactory 中的 populateBean() 方法进行属性赋值

      1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
        
    5. AbstractAutowireCapableBeanFactory 中的 initializeBean() 初始化对象

      1. protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
        
  6. 解决

    1. spring解决循环依赖的诀窍就在于singletonFactories这个三级cache
    2. 这个cache的类型是ObjectFactory,这里就是解决循环依赖的关键,发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)
    3. 这哦对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以spring此时将这个对象提前曝光出来让大家认识,让大家使用
  7. 二级缓存到底能不能解决

    1. 二级缓存也是能够实现的,如果自己想要实现,那么就得重写AbstractAutowireCapableBeanFactory的doCreateBean的方法

    2.      //添加到三级缓存
        if (earlySingletonExposure) {
         if (logger.isTraceEnabled()) {
          logger.trace("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
         }
         addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
         //从三级缓存中取出立刻放入二级缓存
         getSingleton(beanName, true);
        }
      
    3. 如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了spring的设计原则

    4. spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postPocessAfterInitialization方法中对初始化后的Bean完成AOP代理

    5. 如果出现了循环依赖,那没有办法,只能给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖三级缓存是指在实例化Bean的过程中,Spring框架内部维护了三个缓存Map,分别是singletonObjects、earlySingletonObjects和singletonFactories。 1. singletonObjects缓存:存放已经完全初始化的Bean实例,包括在初始化过程中有循环依赖的Bean 2. earlySingletonObjects缓存:存放已经进行了实例化但是还未完成初始化的Bean实例,也包括在初始化过程中有循环依赖的Bean 3. singletonFactories缓存:存放Bean实例的Factory对象,用于解决循环依赖的问题,当需要解决循环依赖时,由Factory对象提供未完成的Bean实例。 通过这三个缓存Spring框架可以在Bean实例化的过程中解决循环依赖的问题。当Bean A依赖Bean B,同时Bean B也依赖Bean A时,Spring框架会通过缓存机制来避免无限递归的循环依赖。具体流程如下: 1. 当需要实例化Bean A时,Spring框架会先从singletonObjects缓存中查找是否已经存在该Bean实例,如果存在,则直接返回该实例。 2. 如果singletonObjects缓存中不存在Bean A实例,则会从earlySingletonObjects缓存中查找是否存在该实例。如果存在,则将其提供给Factory对象创建Bean A实例并返回。 3. 如果earlySingletonObjects缓存中也不存在Bean A实例,则会从singletonFactories缓存中查找是否存在该实例的Factory对象,如果存在,则使用该Factory对象创建Bean A实例并返回。 4. 如果singletonFactories缓存中也不存在Bean A实例的Factory对象,则需要创建一个新的Factory对象,同时将其存放到singletonFactories缓存中。这个新的Factory对象会提供一个未进行初始化的Bean A实例,同时缓存到earlySingletonObjects中。 5. 当Bean A实例被完全初始化后,会将其从earlySingletonObjects缓存中移动到singletonObjects缓存中,以供其他Bean依赖使用。 通过三级缓存的机制,Spring框架可以解决循环依赖的问题,并在Bean实例化过程中避免无限递归的循环依赖

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值