加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

简单了解Spring循环依赖解决过程

发布时间:2020-05-24 00:13:19 所属栏目:Java 来源:互联网
导读:简单了解Spring循环依赖解决过程 这篇文章主要介绍了简单了解Spring循环依赖解决过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知

这篇文章主要介绍了简单了解Spring循环依赖解决过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚。本文就试着尽自己所能,对此做出一个较详细的解读。另,需注意一点,下文中会出现类的实例化跟类的初始化两个短语,为怕园友迷惑,事先声明一下,本文的实例化是指刚执行完构造器将一个对象new出来,但还未填充属性值的状态,而初始化是指完成了属性的依赖注入。

一、先说说Spring解决的循环依赖是什么

Java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖。

构造器的循环依赖就是在构造器中有属性循环依赖,如下所示的两个类就属于构造器循环依赖:

@Service
public class Student {
  @Autowired
  private Teacher teacher;

  public Student (Teacher teacher) {
    System.out.println("Student init1:" + teacher);
  }

  public void learn () {
    System.out.println("Student learn");
  }
}
@Service
public class Teacher {
  @Autowired
  private Student student;

  public Teacher (Student student) {
    System.out.println("Teacher init1:" + student);

  }

  public void teach () {
    System.out.println("teach:");
    student.learn();
  }
}

这种循环依赖没有什么解决办法,因为JVM虚拟机在对类进行实例化的时候,需先实例化构造器的参数,而由于循环引用这个参数无法提前实例化,故只能抛出错误。

Spring解决的循环依赖就是指属性的循环依赖,如下所示:

@Service
public class Teacher {
  @Autowired
  private Student student;

  public Teacher () {
    System.out.println("Teacher init1:" + student);

  }

  public void teach () {
    System.out.println("teach:");
    student.learn();
  }

}
@Service
public class Student {
  @Autowired
  private Teacher teacher;

  public Student () {
    System.out.println("Student init:" + teacher);
  }

  public void learn () {
    System.out.println("Student learn");
  }
}

测试扫描类:

 @ComponentScan(value = "myPackage")
 public class ScanConfig {

 }

测试启动类:

public class SpringTest {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanConfig.class);
    applicationContext.getBean(Teacher.class).teach();

  }
}

测试类执行结果:

 Student init:null
 Teacher init:null
 teach:
 Student learn

可以看到,在构造器执行的时候未完成属性的注入,而在调用方法的时候已经完成了注入。下面就一起看看Spring内部是在何时完成的属性注入,又是如何解决的循环依赖。

二、循环依赖与属性注入

1、对于非懒加载的类,是在refresh方法中的 finishBeanFactoryInitialization(beanFactory) 方法完成的包扫描以及bean的初始化,下面就一起追踪下去。

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 其他代码

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
  }

可以看到调用了beanFactory的一个方法,此处的beanFactory就是指我们最常见的那个DefaultListableBeanFactory,下面看它里面的这个方法。

2、DefaultListableBeanFactory的preInstantiateSingletons方法

public void preInstantiateSingletons() throws BeansException {

    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 判断为非抽象类、是单例、非懒加载 才给初始化
        if (isFactoryBean(beanName)) {
          // 无关代码(针对FactoryBean的处理)
        }
        else {
          // 重要!!!普通bean就是在这里初始化的
          getBean(beanName);
        }
      }
    }

    // 其他无关代码
  }

可以看到,就是在此方法中循环Spring容器中所有的bean,依次对其进行初始化,初始化的入口就是getBean方法

3、AbstractBeanFactory的getBean跟doGetBean方法

追踪getBean方法:

 public Object getBean(String name) throws BeansException {
 return doGetBean(name,null,false);
 }

可见引用了重载的doGetBean方法,继续追踪之:

protected <T> T doGetBean(final String name,@Nullable final Class<T> requiredType,@Nullable final Object[] args,boolean typeCheckOnly) throws BeansException {

    final String beanName = transformedBeanName(name);
    Object bean;

     // 方法1)从三个map中获取单例类
    Object sharedInstance = getSingleton(beanName);
    // 省略无关代码
    }
    else {
      // 如果是多例的循环引用,则直接报错
      if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
      }
      // 省略若干无关代码
      try {
        // Create bean instance.
        if (mbd.isSingleton()) {
          // 方法2) 获取单例对象
          sharedInstance = getSingleton(beanName,() -> {
            try { //方法3) 创建ObjectFactory中getObject方法的返回值
              return createBean(beanName,mbd,args);
            }
            catch (BeansException ex) {
              // Explicitly remove instance from singleton cache: It might have been put there
              // eagerly by the creation process,to allow for circular reference resolution.
              // Also remove any beans that received a temporary reference to the bean.
              destroySingleton(beanName);
              throw ex;
            }
          });
          bean = getObjectForBeanInstance(sharedInstance,name,beanName,mbd);
        }
     }
    // 省略若干无关代码
    return (T) bean;
  }

该方法比较长,对于解决循环引用来说,上面标出来的3个方法起到了至关重要的作用,下面我们挨个攻克。

3.1) getSingleton(beanName)方法: 注意该方法跟方法2)是重载方法,名字一样内部逻辑却大相径庭。

protected Object getSingleton(String beanName,boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);// 步骤A
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
        singletonObject = this.earlySingletonObjects.get(beanName);// 步骤B
        if (singletonObject == null && allowEarlyReference) {
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);// 步骤C
          if (singletonFactory != null) {
            singletonObject = singletonFactory.getObject();
            this.earlySingletonObjects.put(beanName,singletonObject);
            this.singletonFactories.remove(beanName);
          }
        }
      }
    }
    return singletonObject;
  }

通过上面的步骤可以看出这三个map的优先级。其中singletonObjects里面存放的是初始化之后的单例对象;earlySingletonObjects中存放的是一个已完成实例化未完成初始化的早期单例对象;而singletonFactories中存放的是ObjectFactory对象,此对象的getObject方法返回值即刚完成实例化还未开始初始化的单例对象。所以先后顺序是,单例对象先存在于singletonFactories中,后存在于earlySingletonObjects中,最后初始化完成后放入singletonObjects中。

当debug到此处时,以上述Teacher和Student两个循环引用的类为例,如果第一个走到这一步的是Teacher,则从此处这三个map中get到的值都是空,因为还未添加进去。这个方法主要是给循环依赖中后来过来的对象用。

3.2)getSingleton(String beanName,ObjectFactory<?> singletonFactory)方法:

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) {
        // 省略无关代码
        beforeSingletonCreation(beanName); // 步骤A
        boolean newSingleton = false;
        // 省略无关代码
        try {
          singletonObject = singletonFactory.getObject();// 步骤B
          newSingleton = true;
        }
        // 省略无关代码
        finally {
          if (recordSuppressedExceptions) {
            this.suppressedExceptions = null;
          }
          afterSingletonCreation(beanName);// 步骤C
        }
        if (newSingleton) {
          addSingleton(beanName,singletonObject);// 步骤D
        }
      }
      return singletonObject;
    }
  }

获取单例对象的主要逻辑就是此方法实现的,主要分为上面四个步骤,继续挨个看:

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读