What is a cyclic dependency problem

Circular dependencies are circular references, where two or more beans hold each other and form a closed loop. For example, A depends on B, B depends on C, and C depends on A.

How does Spring solve the problem of loop dependencies

In short, Spring uses level 3 caching to solve the problem of loop dependency, which looks like this:

/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<? >> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);Copy the code

The three-level cache is respectively

  • SingletonObjects: singleton Bean cache;

  • SingletonFactories: singleton Bean factory cache;

  • EarlySingletonObjects: a cache of pre-exposed singleton beans;

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; }Copy the code

The general process for obtaining a singleton Bean is as follows:

  • Firstly, singleton beans are obtained from the first-level cache singletonObjects through beanName.
  • If the level of the cache does not exist the bean and isSingletonCurrentlyInCreation return to true, then through earlySingletonObjects access to early exposure of bean;
  • If the bean does not exist in the secondary cache and is allowed to fetch objects with singletonFactories, then the bean is created through the factory.

IsSingletonCurrentlyInCreation indicates whether the current bean is created, such as object through the constructor depends on B, access to objects in the process of filling properties, found in the form of A member variable dependent object B, so need to instantiate B first, in this case, The state of object A is being created.

To understand how Spring solves the problem of loop dependency, you also need to understand the initialization process of Spring beans, which can be roughly divided into the following processes

  • Call the constructor to instantiate;
  • Populate object properties;
  • Initialize using init method

After the first step in creating the Bean, the object has been produced, with some properties missing, but the object can be referenced.

The key to loop dependency is the tertiary cache, where the singletonFactories are populated in the method addSingletonFactory,

	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);
			}
		}
	}
Copy the code

This method is called the doCreateBean AbstractAutowireCapableBeanFactory class, then after the bean instantiation, which invokes the constructor, singletonFactories has already been registered, With singletonFactories, Spring exposes half-initialized objects for use by other classes.

If object A depends on object B as A member variable, and object B depends on object A as A loop dependency, let’s look at Spring’s steps to resolve the loop dependency

  • Object A is initialized, first instantiated through the constructor, and then populated with properties. It is found that the dependent object B is not loaded.
  • Object B is initialized, first instantiated through the constructor, and then populated with properties. At this point, object A is exposed ahead of time, and object B is initialized.
  • Object A completes the initialization by filling in the attributes of object B.

For example, object A depends on object B through the constructor, and object B depends on object A through the constructor. In this case, Spring cannot do anything because object A and object B depend on object A through the constructor. There is no way to expose itself in advance by doing initialization in the constructor.