快上网专注成都网站设计 成都网站制作 成都网站建设
成都网站建设公司服务热线:028-86922220

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

SpringIOCDI循环依赖实例详解

要弄清楚循环依赖

成都创新互联公司是一家专注于成都做网站、网站建设、外贸营销网站建设与策划设计,新华网站建设哪家好?成都创新互联公司做网站,专注于网站建设十余年,网设计领域的专业建站公司;建站业务涵盖:新华等地区。新华做网站价格咨询:028-86922220

1、需要知道Bean初始化的两个阶段

① Bean实例化创建实例对象(new Bean())

② Bean实例对象初始化(DI:注解自动注入)

2、DefaultSingletonBeanRegistry类中的5个容器

/** 记录已将创建的单例 */
  private final Map singletonObjects = new ConcurrentHashMap<>(256);

  /** 记录singletonFactory singeletonFactory中存放beanName和上面的①阶段的bean:Bean实例化实例对象(还未初始化DI)*/
  private final Map> singletonFactories = new HashMap<>(16);

  /** 记录早期的singletonBean 存放的也是① */
  private final Map earlySingletonObjects = new HashMap<>(16);

  /** 存放已经初始化后的beanName,有序 */
  private final Set registeredSingletons = new LinkedHashSet<>(256);

  /** 记录正在初始化的bean的beanName */
  private final Set singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

其中跟循环依赖相关的是singletonFactories、singeletonsCurrentlyInCreation、earlysingletonObjects.

3、循环依赖实现

①bean初始化前后会打标,加入到singletonsCurrentlyInCreation容器中,这个打标会在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) */
  public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    ...
    //循环依赖相关:初始化前先singletonsCurrentlyInCreation.add(beanName)
    beforeSingletonCreation(beanName);
    ...
    //lamda表达式:其实是调用createBean(beanName, mbd, args):Bean初始化
    singletonObject = singletonFactory.getObject();
    ...
    //循环依赖相关:初始化后singletonsCurrentlyInCreation.remove(beanName)
    afterSingletonCreation(beanName);
    ...//初始化完后
    //this.singletonObjects.put(beanName, singletonObject);放入到单例容器中
    //this.singletonFactories.remove(beanName);清空循环依赖的两个打标
    //this.earlySingletonObjects.remove(beanName);
    //this.registeredSingletons.add(beanName);放入单例beanName容器中
    addSingleton(beanName, singletonObject);
    ...
    }
  }

② 上面singletonObject = singletonFactory.getObject()时向singletonFactories中记录了(new Bean()),singletonFactories也会在核心方法getSingleton()中起作用

/* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean */
  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    ...
    //循环依赖相关逻辑:
    //this.singletonFactories.put(beanName, singletonFactory);
    //将实例化bean(①阶段)、beanName组装成singletonFactory装入singletonFactories容器
    //this.earlySingletonObjects.remove(beanName);
    //删除earlySingletonObjects中beanName
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    ...
    //实例初始化 就是在这里面实现依赖注入DI的:反射实现
    //调用AutowiredAnnotationBeanPostProcessor.postProcessProperties
    populateBean(beanName, mbd, instanceWrapper);
    ...
  }

③ 核心方法getSingleton

/* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) */
  protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
     //循环依赖核心就在于这个判断,由于打标+记录了①阶段的bean,
    //循环依赖第二次调用getBean("a")时,这里会直接返回第一次调用getBean("a")创建的①阶段的bean
    //而不会调用createBean("a")再次bean初始化(造成两个bean的循环创建)
    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;
  }

④ 循环依赖流程

/* org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */
  protected  T doGetBean(final String name, @Nullable final Class requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    ...
    //假设A、B互相依赖
    //第一次getBean(A),sharedInstance == null,走else,createBean
    //A正在创建打标,①中beforeSingletonCreation()
    //A实例化后保存到singletonFactories中②中addSingletonFactory(beanName,singletonFactory)
    //DI依赖注入:②中populateBean(beanName,mbd,instanceWrapper),发现依赖B,调用getBean(B)初始化B的单例
    //调用getBean(B)重复上面步骤,DI依赖注入发现依赖A,调用getBean(A)
    //第二次getBean(A),③中if(singletonObject == null && isSingletonCurrentlyInCreation(A))由于打标了所以返回singleFactory.getObject()
    //下面if条件直接返回bean,没有走else破坏了循环
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      //
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
      ...
      //
      createBean(beanName, mbd, args);
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    return bean;      
  }

四、总结

未看源码之前,其实对循环依赖有一个想法:循环依赖可以看做是一个死锁。

预防死锁的方法:打破死锁的四个必要条件(互斥、请求并等待、不可剥夺、循环等待),由于循环依赖的资源是对象自身,所以常用破坏循环等待条件方法:编号顺序执行,不适用

选择破坏请求并等待条件:先创建对象,再赋值,模型

A a = new A();
B b = new B();
A.b = b;
B.a = a;

研究源码之后发现:想法差不多,但是代码实现非常精彩。模型(打标没想到过)

A a = new A();
B b = new B();
b.a = a;
a.b = b;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。


网站栏目:SpringIOCDI循环依赖实例详解
网址分享:http://6mz.cn/article/jjohds.html

其他资讯