1. What are circular dependencies

In the process of bean property injection, A depends on B, and B depends on A

What happens in code:

@Component
public class A {
    // insert B into A
 @Autowired
 private B b;
}

@Component
public class B {
    // A is injected into B
 @Autowired
 private A a;
}

// Rely on yourself
@Component
public class A {
    // insert A into A
 @Autowired
 private A a;
}
Copy the code

2. When can circular dependencies be handled

  • Spring’s singleton bean loop dependency can be resolved. Multi-instance bean dependencies, where a new bean is created each time, may run out of memory, so Spring detects this and packages exceptions directly.

  • Dependency injection can’t be all constructor injection, for example:

@Component
public class A {
// @Autowired
// private B b;
 public A(B b) {}}@Component
public class B {

// @Autowired
// private A a;

 public B(A a){}}Copy the code

The way B is injected into A is through the constructor, and the way A is injected into B is through the constructor. This loop dependency cannot be resolved. If your project has two such interdependent beans, an error will be reported at startup

3. How does Spring solve loop dependencies

3.1 The Absence of AOP:

Spring’s process for creating beans:

  • Instantiation, new one object (simple) corresponding methods: AbstractAutowireCapableBeanFactory createBeanInstance in method
  • The object, attribute injection, (to new properties filling) corresponding methods: AbstractAutowireCapableBeanFactory populateBean method
  • Initialization, execution aware interface, initialization method) corresponding methods: AbstractAutowireCapableBeanFactory initializeBean

3.1.1 getBean () method

The object is created by calling the getBean () method, which does the following:

  • Retrieves the created bean from the cache
  • If not in the cache, create a new bean

3.1.2 getSingleton (beanName) method

The getBean() method calls getSinglteon(beanName), and the getSingleton(beanName) method calls getSingleton(beanName, The getSingleton(beanName, true) method attempts to fetch objects from the tertiary cache.

Three levels of cache

1. SingletonObjects: level 1 cache, which stores all created singleton beans

2. EarlySingletonObjects: objects that have been instantiated but have not been injected or initialized

3. SingletonFactories, a pre-exposed singleton factory from which objects are stored in the level 2 cache

The first time you don’t get the bean in the cache, you go to the getSingleton(beanName, singletonFactory) method.

3.1.3 getSingleton(beanName, singletonFactory) method

This method is used to create beans, which are then put into the level-1 cache singletonFactories

3.1.4 Placing it in level 3 Cache

When a bean is instantiated, before initialization, it is wrapped as a factory and added to the three-level cache singletonFactories.

// The argument passed here is also a lambda expression, () -> getEarlyBeanReference(beanName, MBD, bean)
protected void addSingletonFactory(String beanName, ObjectFactory
        singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            // Add to level 3 cache
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName); }}}Copy the code

After A is created and instantiated, it will be put into the three-level cache, and then the attribute injection will start. During the attribute injection process,B will be instantiated if it is found to be dependent on B. After the instantiation,B will inject the attribute of B, and if it is found to be dependent on A, the uninitialized A will be obtained from the three-level cache.

A injected into B is an object that has been exposed in advance through the getEarlyBeanReference method, and is not yet A full Bean

3.1.5 AOP’s cyclic dependencies

Using level 3 cache instead of level 2 cache is not just because level 3 cache is the only way to solve the circular reference problem, but level 2 cache is also very good at solving the circular reference problem. Using a three-level cache instead of a two-level cache is not for IOC’s sake, but for AOP’s sake, i.e. in the case of AOP, the original object is injected into other beans instead of the final proxy object.

This is not to say that the second level cache cannot inject proxy objects if AOP exists. The essence is that the original Spring did not solve the problem of circular reference. The design principle is to generate AOP objects after bean instantiation, property setting, and initialization. But in order to solve the loop dependency without breaking this design principle, a third-level cache with functional interfaces is used; With second-level caching, AOP’s proxy work can be advanced to the stage where instances are exposed ahead of time; That is, all beans are created as proxy objects and re-initialized and other work; But in doing so, contrary to spring’s AOP design principles, aop implementation needs to be separated from the normal life cycle creation of beans; This only uses the third level cache to encapsulate a functional interface object into the cache, triggering the generation of proxy classes when a cyclic dependency occurs.