1. What are circular dependencies

When I first started learning Spring, I was unfamiliar with loop dependencies and caching and always thought it was hard to understand, but it’s not that complicated.

For example, in the class of Teacher, both teachers have students, so students can be regarded as a Teacher attribute. In the same way, a student can have a teacher. So the student class can also have the teacher attribute.

public class Teacher{
    public Student student;
}

public class Student{
    public Teacher teacher;
}
Copy the code

When we create a Teacher object, we assign the Student property to the Teacher object, which also assigns the Teacher property……

So these two classes, you have me and you have me.

Finally achieve perpetual motion machine.

2. The life cycle of the SpringBean

We had a problem with loop dependencies when we adopted Spring. Spring also has the ability to automatically create beans, so we can’t solve the problem of loop dependency until we understand how Spring actually creates beans.

Here’s a simple list of steps that are useful for now:

A normal SpringBean lifecycle:

  • Generate containers as required

  • Create object A (createBeanInstance)

  • If you assign A value to A property, it will look in the pool to see if there is an object. If there is, it will assign A value. If there is not, it will create A (populateBean).

  • AOP operation

  • Add proxy objects to the pool

What about creating objects with a loop dependency?

And so the constant recursive calls just keep on going.

3. How to solve circular dependencies

Here we consider adding a cache.

The concept of a three-level cache

Level 1 cache: SingletonObject — Places objects that have just completed their life cycle

Level 2 cache: EarlySingletonObject – Places objects that have not yet completed their life cycle after being created

Level 3 cache: SingletonFactory — object factory, used to create objects

In layman’s terms, a cyclic dependency circulates over and over because it creates new objects.

We don’t have to keep creating them, we just have to get to teacher: student, student: teacher, we don’t have to go on layer by layer. So it doesn’t matter if it’s followed by null. So let’s just let A put A null value of this object into the cache right after the shell is created. We just need to make sure we have this object.

Because you can’t put a reference in a level 1 cache, because level 1 cache requires you to put it in after the object is created, and obviously level 2 cache is better, because level 2 cache can put a reference in before the object is created. We don’t need it to be exact, we just need an object to break the loop.

It’s not that simple

This seems to be solved our problem, everything is happy, ready to call it a day, this time there are problems……

When we create the SpringBean, once we use the dynamic proxy, the above will not work.

Because AOP using the dynamic proxy, as shown in figure, created A shell, A assignment to B, no will to create A B B, if we use the proxy object, the proxy class and original class is not A thing, so the assignment of time to find you won’t find the original object, can only continue to create new proxy objects.

This is uncomfortable. How do we solve the problem of dynamic proxies changing to proxy objects?

Level 3 cache solves the problem of loop dependency

Here’s how Spring solves the problem.

  • First add A property creatingAdd(” Created object A”);
  • Instantiate to get A raw object —-> into level 3 cache
  • Assign A value to B of attribute A —-> search for B in singleton pool, cannot find instance —-> create B
    • Instantiate the B primitive object
    • Assign A value to A —-> found in singleton pool —-> not found Check creatingAdd (” A “) — – > found A circular dependencies appeared — — — – > find — — — — > go to the second level cache to triple the cache to find — — — – > get original object — — — — > A of AOP — — — – > generate A proxy object — — — – > in the second level cache
    • Assign values to other attributes
    • The other work
    • Put into the singleton pool
  • Assign values to other attributes
  • The other work
  • Get object A from level 2 cache
  • Put into the singleton pool
  • Remove the creatingAdd(” Created object A”) attribute of A

CreatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd: creatingAdd

Our final object will still be in the singleton pool, which is level 1 cache. We created the original object of A and put it into the third-level cache this time. Spring found the phenomenon of cyclic dependence and will search for it in the second-level cache and third-level cache, mainly to get the original object and target to generate the dynamic proxy object. This way we can put the generated proxy object into level 2 cache, and then do something else to put the final object into level 1 cache.