Simply understand the spring circular dependency resolution process

This article mainly introduces a simple understanding of the spring circular dependency resolution process. It is introduced in great detail through the example code, which has a certain reference value for everyone's study or work. Friends in need can refer to it

preface

Speaking of the solution of circular dependency in spring, I believe many garden friends know more or less, but when they really want to explain it in detail, they may not be able to explain it clearly. This paper tries to do its best to make a more detailed interpretation of this. In addition, it should be noted that two phrases, class instantiation and class initialization, will appear below. In order to avoid being confused by garden friends, it should be stated in advance that instantiation in this paper refers to the state that an object is new after the constructor has just executed, but the attribute value has not been filled, and initialization refers to the completion of attribute dependency injection.

1、 First, what is the circular dependency solved by spring

There are two kinds of circular dependencies in Java, one is the circular dependency of constructor, and the other is the circular dependency of attribute.

The circular dependency of the constructor is that there are attribute circular dependencies in the constructor. The following two classes belong to the circular dependency of the constructor:

@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();
  }
}

There is no solution to this circular dependency, because the JVM virtual machine needs to instantiate the constructor parameter before instantiating the class. Because the circular reference parameter cannot be instantiated in advance, it can only throw an error.

The circular dependency solved by spring refers to the circular dependency of attributes, as shown below:

@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");
  }
}

Test scan class:

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

 }

Test startup class:

public class SpringTest {

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

  }
}

Test class execution results:

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

You can see that the property injection is not completed when the constructor executes, but has been completed when the method is called. Let's take a look at when spring completes attribute injection and how to solve circular dependency.

2、 Cyclic dependency and attribute injection

1. For non lazy loaded classes, the package scanning and bean initialization completed by the finishbeanfactoryinitialization (bean factory) method in the refresh method are tracked below.

protected void finishbeanfactoryInitialization(ConfigurableListablebeanfactory beanfactory) {
    // 其他代码

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

You can see that a method of beanfactory is called. The beanfactory here refers to the most common defaultlistablebeanfactory. Let's look at the method in it.

2. Preinstantiatesingletons method of defaultlistablebeanfactory

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);
        }
      }
    }

    // 其他无关代码
  }

You can see that in this method, you cycle all beans in the spring container and initialize them in turn. The initialization entry is the getBean method

3. GetBean and dogetbean methods of abstractbeanfactory

Trace getBean method:

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

It can be seen that the overloaded dogetbean method is referenced. Continue to track it:

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;
  }

This method is relatively long. The three methods marked above play a vital role in solving circular references. Let's tackle them one by one.

3.1) getsingleton (beanname) method: note that this method and method 2) are overloaded methods with the same name but different internal logic.

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;
  }

You can see the priority of these three maps through the above steps. Singletonobjects stores singleton objects after initialization; Earlysingletonobjects stores an early singleton object that has been instantiated but not initialized; In singletonfactories, the objectfactory object is stored. The return value of the GetObject method of this object is the singleton object that has just completed instantiation and has not started initialization. Therefore, the order is that the singleton objects first exist in singletonfactories, then in earlysingletonobjects, and finally in singletonobjects after initialization.

When debugging here, take the above two circular reference classes of teacher and student as an example. If teacher is the first to go to this step, the values obtained from these three maps are empty because they have not been added. This method is mainly used for later objects in circular dependency.

3.2) getsingleton (string beanname, objectfactory singletonfactory) method:

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;
    }
  }

The main logic of obtaining singleton object is realized by this method, which is mainly divided into the above four steps. Continue to look at them one by one:

Step a:

protected void beforeSingletonCreation(String beanName) {
    // 判断,并首次将beanName即teacher放入singletonsCurrentlyInCreation中
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
  }

Step C:

protected void afterSingletonCreation(String beanName) {
    // 得到单例对象后,再讲beanName从singletonsCurrentlyInCreation中移除
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
  }

Step D:

protected void addSingleton(String beanName,Object singletonObject) {
    synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName,singletonObject);//添加单例对象到map中
      this.singletonFactories.remove(beanName);//从早期暴露的工厂中移除,此map在解决循环依赖中发挥了关键的作用
      this.earlySingletonObjects.remove(beanName);//从早期暴露的对象map中移除
      this.registeredSingletons.add(beanName);//添加到已注册的单例名字集合中
    }
  }

Step B:

Here, the GetObject method of objectfactory is called. Where is this method implemented? What did you return? Go back and find method 3 in 3. Those who have a good understanding of java8 functional programming should be able to see that the return value of method 3 [createbean (beanname, args)] is the return value of GetObject method, that is, the single instance object we need is returned by method 3. Go down and track method 3.

3.3) abstractautowirecapablebeanfactory #createbean (java.lang.string, org.springframework.beans.factory.support.rootbeandefinition, Java. Lang.Object []) method

protected Object createBean(String beanName,RootBeanDeFinition mbd,@Nullable Object[] args)
      throws BeanCreationException {

    // 省略无关代码
    try {
      Object beanInstance = doCreateBean(beanName,mbdToUse,args);
      return beanInstance;
    }
    // 省略无关代码
  }

After removing irrelevant code, the only key method is the docreatebean method, which is tracked as follows:

protected Object doCreateBean(final String beanName,final RootBeanDeFinition mbd,final @Nullable Object[] args)
      throws BeanCreationException {

    BeanWrapper instanceWrapper = null;
    // 省略代码
    if (instanceWrapper == null) {
      // 实例化bean
      instanceWrapper = createBeanInstance(beanName,args);
    }
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
      // 重点!!!将实例化的对象添加到singletonFactories中
      addSingletonFactory(beanName,() -> getEarlyBeanReference(beanName,bean));
    }
    // 初始化bean
    Object exposedObject = bean;
    try {
      populateBean(beanName,instanceWrapper);//也很重要
      exposedObject = initializeBean(beanName,exposedObject,mbd);
    }
    // 省略无关代码
    return exposedObject;
}

The key points marked in the above notes are the key to this method. In the addsingletonfactory method, the second parameter objectfactory is stored in singletonfactories, which can be called when other objects depend on it. Then, the following populatebean method performs attribute injection on the newly instantiated bean (this method has many associations. This article will not start tracking for the time being, and interested garden friends can check it by themselves). If they encounter an object attribute in spring, they can obtain the object through the getBean method. So far, the processing process of circular dependency in spring has been traced. Let's summarize it below.

Summary

Attribute injection is mainly carried out in the populatebean method. For circular dependency, take the example of student injected into teacher and teacher injected into student in the above as an example. It is assumed that the loading order of spring is to load teacher first and then student.

After the getBean method triggers the initialization of Teacher:

a. First, go to method 1) in 3. At this time, the map is empty and no instance can be obtained;

b. Then go to method 2). Step a, step C and step d are the methods to control the data in the map. They are simple to implement and can be ignored temporarily. The GetObject method in step B triggers the call to method 3);

c. In method 3), first instantiate the teacher object through createbeaninstance, and then put the instantiated object into singletonfactories through addsingletonfactory method to complete the early exposure of the teacher object;

d. Then, in method 3), the attribute of the teacher object is injected through the populatebean method. If it is found that it has a student attribute, the getBean method is triggered to initialize the student

e. Repeat steps a, B and C, except that the student object needs to be initialized at this time

f. When you reach D, call populatebean to inject attributes into the student object. If you find that it has a teacher attribute, trigger the getBean method to initialize the teacher;

g. Initialize the teacher and then go to a, but the map is not empty at this time, because the teacher instance has been put into singletonfactories in step C, and a returns after getting the teacher instance;

h. Complete the initialization of student in F, and then backtrack up to complete the initialization of teacher;

After the initialization of teacher is completed, the initialization of student is simple, because the singleton has been saved in the map.

So far, the summary and analysis of spring's circular dependency is completed. To sum up, spring solves the problem of circular dependency by exposing the instantiated objects to singletonfactories in the spring container in advance.

The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>