zoukankan      html  css  js  c++  java
  • Spring是如何解决循环依赖(引用)的?

    首先什么是循环依赖,比如A->B->A,正常我们普通的类怎么解决呢,就是先  new A(),new B() ,然后再 setB,setA ,也比较好解决。

    那么大家为什么会经常提到spring循环依赖呢,我认为主要有两个原因:

    1. spring的bean是一个一个加载的,A创建的过程中,发现依赖B,B还没创建,就会去先创建B
    2. spring的bean有一个较复杂的生命周期,它的bean常常需要被生成代理

    那spring是如何解决这个循环依赖问题的呢?

    spring的核心思想同样是:先实例化,后设置属性。它内部维护了三个map,就是网上常说的三级缓存

    一个是 singletonObjects  map ,也就是我们说的单例池,它存放的是已经完成实例化的完成的bean;

    一个是 singletonFactories  map,二级缓存,它是解决循环依赖的关键,也就是存放未完全创建的对象工厂ObjectFactory,它持有原对象的引用;

    一个是 earlySingletonObjects  map,三级缓存,它是存放经过ObjectFactory.getObject,生成的早期单例对象存在这个map中,这里面的对象可能是个代理类。

     singletonFactories  、 earlySingletonObjects  中的对象都还不是完整的bean,都还没有进行属性的注入,以及初始化过程。

    注:很多文章会称 singletonFactories 为三级缓存, earlySingletonObjects 为二级缓存,这里其实没有谁对谁错,只是个叫法,不用纠结。

    说明:原型(Prototype)的场景、通过构造方法进行依赖注入,这两种情况循环依赖都没有解决

    spring的解决循环依赖的大概过程

     创建A,先把A通过构造方法反射实例化得到一个空的对象,然后根据是否单例,是否允许循环引用,如果是的话,包装一个 ObjectFactory 对象,加到 singletonFactories  map中,然后进行A的属性设置,这时候发现A的属性B还没创建,转而先去创建B,同样的,先把B加到 singletonFactories  map中,设置B的属性的时候,发现A还没创建,又转而去创建A,此时A已经在 singletonFactories ,然后调用A的ObjectFactory中的getObject方法,获取A的早期引用(生成的可能是个代理对象,此时A还没实例完全,属性还没设置),然后会把A转移到earlySingletonObjects map中(),接着继续执行B的属性填充等初始化过程,B的创建过程完成,将B加到单例池中。B创建完之后,回到A的创建过程,进行属性填充,将B设置到A上,完成A的初始化过程,完后将A从earlySingletonObjects中取出,加到singletonObjects 单例池中。至此,循环依赖过程结束后。

     为什么第二级缓存要使用ObjectFactory?

    为了这个bean能够被一些beanPostProcessor处理,使其能够创建为代理对象。

    三级缓存earlySingletonObjects 的作用是什么?

    存储ObjectFactory.getObject()之后生成的半成品对象(这个对象只是原本的实例化,或者代理对象,没有经历过属性注入与初始化过程),用于保存真实的对象引用,因为真正要放到单例池中的对象可能是个代理对象

     postProcessAfterInitialization也可以生成代理,ObjectFactory.getObject()生成代理后,再执行postProcessAfterInitialization,岂不是会执行两遍生成代理流程?

    debug发现,ObjectFactory.getObject()返回的确实是代理对象(如果需要的话),后续在执行postProcessAfterInitialization的时候,会有个earlyProxyReferences的判断,直接返回原对象,在后面流程中,会从earlySingletonObjects 中取代理后的对象,放到单例池中。

    说明,debug发现

    1. B只会经过singletonFactoriessingletonObjects ,不经过earlySingletonObjects
    2. 如果A需要生成代理对象,那生成的代理对象中的属性值为null,如:A->B->$proxyA->b=null,单例池中存放的是代理后的A
    3. 如果A不需要生成代理对象,那么关系就是 A->B->A->B....,形成循环依赖

     关键源码:

    注:只列出与循环依赖相关的关键代码

    // org.springframework.beans.factory.support.AbstractBeanFactory
    
    protected <T> T doGetBean(
                String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
                throws BeansException {
      
          // 获取单例bean,首次进来为null
            Object sharedInstance = getSingleton(beanName);
            if (sharedInstance != null && args == null) {
                ...
            } else {
          ...
          // Create bean instance.
                    if (mbd.isSingleton()) {
              // 获取单例对象,没有则执行创建,第二个参数为ObjectFactory
                        sharedInstance = getSingleton(beanName, () -> {
                            try {
                  // 创建bean
                                return createBean(beanName, mbd, args);
                            } catch (BeansException ex) {
                                destroySingleton(beanName);
                                throw ex;
                            }
                        });
                    }
        }
    }
    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
    
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {
    ...
      if (instanceWrapper == null) {
            // 实例化bean
                instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
    ...
      // 是否单例,是否可以循环引用,是否是正在创建中的单例对象
      boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
          // 提前暴露工厂对象,将ObjectFactory添加到singletonFactories中
         // getEarlyBeanReference返回的对象会经过一些BeanPostProcessor的处理,可能生成对应的代理对象,如果不需要代理,则基本都是原对象
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }
      
      ...
      // 属性注入,如果发现有依赖的bean,并且未创建,回去先创建依赖的bean
      populateBean(beanName, mbd, instanceWrapper);
      // 初始化回调流程
        exposedObject = initializeBean(beanName, exposedObject, mbd);
      
      ...
      if (earlySingletonExposure) {
                Object earlySingletonReference = getSingleton(beanName, false);
                if (earlySingletonReference != null) {
                    if (exposedObject == bean) 
              // 这个地方会把原对象,替换成代理对象
                        exposedObject = earlySingletonReference;
                    }
          }
      }
    // org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
    
    // 上面的getEarlyBeanReference方法中调用的
    @Override
        public Object getEarlyBeanReference(Object bean, String beanName) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            this.earlyProxyReferences.put(cacheKey, bean);
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    
    // bean生命周期中initializeBean方法中的流程,一般AOP代理也在这边生成
    @Override
        public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 代理对象的话,这边不会进入if  
                if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
        }
    // org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
    public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
      
      /**(一级缓存)已经完成加载的单例缓存.*/
      private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
      /**(三级缓存)早期的单例对象缓存集合. 存储二级缓存加工过的对象,此时该Bean还未构建完成 */
      private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
      /**(二级缓存)单例的工厂Bean缓存集合. */
      private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
      public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        ...
          singletonObject = singletonFactory.getObject();
          newSingleton = true;
        ...
          if (newSingleton) {
            // 添加到单例池中
            addSingleton(beanName, singletonObject);
          }
      }
    
      // 加到二级缓存
      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);
            }
          }
       }
      
      ......
      // 注:高版本的spring,这个方法会稍有不同(有double check的逻辑),思路是一样的
      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;
      }
    }
  • 相关阅读:
    drf3
    字典的操作方法
    列表的操作方法
    字符串的操作方法
    while循环和基本运算符
    初识数据类型
    USDT相关
    带团队
    CentOS7更改时区及同步网络时间
    mac胡刷新dns
  • 原文地址:https://www.cnblogs.com/dong320/p/14529731.html
Copyright © 2011-2022 走看看