Tear Spring source code by hand (3), thoroughly understand the principle of Spring circular dependency

Many friends have probably noticed that I have written some articles to explain it clearly: no other articles on the whole network explain it clearly, or most of the articles are wrong questions. For example: What is the relationship between BASE theory and distributed transactions in "Talking Through Distributed Transactions ".

I also searched for this article before writing, including articles and paid videos. I found that the teacher (or a teacher with a good reputation in this area) that I spent money to learn was repeating a few words over and over again, but he did not explain the problem thoroughly. why? I analyzed it, because the teacher did not explain the problem to be solved by this design from the beginning.

Previous review

This article is the third article in the series of tearing Spring source code. Due to the logical relationship between the contexts, friends who have not read the first two articles are strongly recommended to read the first two articles. The last portal: " Tear the Spring source code by hand (2), thoroughly understand the Spring post-processor ".

Reinforcement is an effective means of deep memory. A brief summary of the previous article. The process of Spring Bean creation is:

bb1319e746b887c2b24d12dff0ad6be1.png

1. Generate Bean definition according to class

2. Instantiate according to Bean

3. Fill in the Bean property of the instance

4. Initialize Bean

5. Bean post-processing

6. Complete the creation of the Bean object

All code texts of this series can be found at https://github.com/xiexiaojing/yuna.

lead to questions

In the last code of the last article, let's do this, let testService refer to userService:

177a20360c94d6beeed029dd839d52bc.png

Plus the original userService references testService:

4d04fff44848d5ebdf5810e0b05e01d9.png

Run the startup class:

8e37816e8fb0d0edac88ffc35673cf2b.png

Circular references cause stack overflow:

7e7a97b1a3d7231fcd979775a7cfeeb5.png

The reason is very clear from the code. There are recursive calls that lead to infinite loop dependencies :

40d4d24f654b7299176bcc6352f945dd.png

What we simulate is the Spring source code, and the principle is the same as the Spring source code. That Spring also had this problem initially. How is it solved?

Singleton pool L2 cache

Spring's solution is also a very conventional idea: testService refers to userService. To create userService, you need to get the Bean of testService first. Can you create a semi-finished product first and use it first? This is Spring's second-level cache idea for solving circular dependencies.

If there is a second level cache, then there is a first level cache. What is the first level cache? In the first two lessons, we introduced that the bean is obtained from singletonObjects, that is, the singleton pool, when the Scope is a singleton. The role of this singleton pool is to cache singleton objects, which is the first level cache.

b6e6ff16cb2aa1a748fcdb42f99028ad.png

Then let's look at the second level cache, also called the semi-finished product pool. The idea is that if the Bean does not exist when it is acquired, it will be acquired from the semi-finished product pool. If you can't get it, instantiate one immediately and put it in the semi-finished product pool. In this way, when other bean creation depends on it, it can be used directly first. Make sure that the instantiation that depends on it succeeds.

Define the semi-finished product pool:

1acdb070692ab52f95d90461a4664b86.png

When obtaining a bean, if it cannot be obtained, instantiate one and put it in the semi-finished product pool:

b2ce1f36a11b74258a56e7664222fcc0.png

After adding these four lines of code in total, let's run it again:

7caf0d8b42a5021dc833e775fa6126a4.png

062018cf367f4bc7a193f63716e6c1eb.png

Problems with the second level cache of the singleton pool

Do you remember that we used to tear up the source code of mybatis? You can review the article " The Essence and Principles of Mybatis ". If you want to take the time to write an article about tearing spring to integrate mybatis, you can leave a message in the comment area if you are interested. This article is over 15, and I want to read it by default. I will get the article out within 8 hours~

When spring integrates mybatis, we define the interface when we use it, and annotate the scan annotation on the interface:

77a562ad213f9c8ffab11bb3e9f24bac.png

Essentially Repository is a Component annotation:

631f5829291f15ac7ccc4e9e5afd3177.png

That's the problem. Mybatis' method of operating sql statements is that interfaces are not classes, so they cannot be instantiated. There is a problem with the early instantiation of the secondary cache. If you don't believe me, let's try it out:

ac117d1d6a295b87e2cedb49adf089c8.png

Inject an interface, and an error is reported when it starts:

58018536dab0b458813325c751ae35a2.png

Because of itself, the Bean we inject using Spring is a proxy class. For example, the interface of mybatis finally uses the proxy class of mybatis after executing the bean post-processor.

The problem is clear: the second-level cache is instantiated in advance due to some interfaces that cannot be instantiated, and an error will be reported.

Singleton pool L3 cache

So how do you solve this problem if you are the designer of Spring? Then if it is found to be an interface, the post processor processor is executed in advance to create the proxy class for use. Not to mention, Spring's designers really did it this way.

First define an interface, which is copied directly from the Spring source code:

8800c86c95733e3b00999d915c12c17d.png

The role of the implementation class of this interface is to implement getObject and replace the interface with a proxy class. Here is the implementation of mybatis. For details, please refer to " The Essence and Principles of Mybatis ":

fa92f98b64824f9dcad88f55ef296c17.png

Essentially Repository is an alias for a Component annotation. In order to make our annotations in spring scannable, let's replace it with the @Component annotation:

c7dde501b311bfd55f617040c9cea01f.png

Because of the location of the class, let's expand the scan range:

0f0445662e39ae124da17b147fd9c596.png

Defining a L3 cache is also called a factory pool:

39cbfb4c8f738538710203e36955aa0f.png

If the interface instantiates the factory pool object first, get the real Bean object from the factory pool object:

58345caf331c6504b46bf92e7f912cee.png

This is just a put operation. In order to be consistent with the Spring source code as much as possible, it is divided into methods separately, and the following judgments are added. In fact, the core is one line:

9f79b69429d66ac060271445a27a958f.png

The other is because it is instantiated in advance. When creating a bean, it is judged that if it has already been instantiated, it does not need to be instantiated again. It is just a judgment:

bfa6aca17d670b81e3c2b1d489153519.png

That's all for the transformation, let's run it:

a71146bf1c18fd06d1a6a07c2681e864.png

a1bc6dd29529c71e5c96501678db4064.gif

Summarize

This article provides Spring's solutions for the two problems encountered when Spring is created: the infinite loop dependency problem and the interface instantiation problem.

Although the code is torn by me, it is not exactly the same as the source code, but the principle is the same. During the interview, I told people that Spring is implemented in this way, and there is no problem at all.

In the first article of this series, "Tear the core source code of spring by hand, thoroughly understand the spring process", in fact, the principles of Spring IoC inversion of control and dependency injection have been explained and implemented. Both articles address the needs and questions on this basis. " Tear Spring Source Code by Hand (2), Thoroughly Understand Spring Post-Processor " solves the problem of enhancing beans. This article is how to solve the problems caused by the diversity of beans.

Spring itself is like this: based on a simple idea, and then solve the problems encountered in the design.

programming for a lifetime

Because the official account platform has changed the push rules, if you don't want to miss the content, remember to click "Watching" after reading it, and add a "star", so that every new article push will appear in your subscription list for the first time.

PDCA methodology , check if you miss the update: I will update the article every Wednesday around 8 pm, if you don't receive it, remember to open the [Programming Life] public account to find it (*^▽^*)

Guess you like

Origin blog.csdn.net/xiexiaojing/article/details/123887371