0 Read before you read
- Do not understand or different opinions, welcome to leave a message to discuss, the message will be back!
- 00 Spring first look at the source code analysis series description
What are cyclic dependencies
Circular dependencies include bean objects Bean1 and Bean2, and bean objects Bean1 and Bean2. Bean2 has a member variable Bean1. The specific code case is as follows:
The code structure is shown below:
There are four classes used, two of which are Bean classes:
@Component
public class Chicken {
@Autowired
Egg egg;
}
Copy the code
@Component
public class Egg {
@Autowired
Chicken chicken;
}
Copy the code
A configuration class:
@ComponentScan("spring.post1.beans")
public class Config {
}
Copy the code
A simple method to start a class is main:
public class DemoSpringCircularDependencies { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); Chicken chicken = ac.getBean("chicken", Chicken.class); System.out.println(chicken); }}Copy the code
As you can see from the code, this chapter focuses on how Spring solves the problem of loop dependencies for beans based on @AutoWired annotations. The two beans that the loop depends on are Chiken (which requires the property Egg) and Egg (which requires the property Chicken).
2. Pre-knowledge
-
Before learning this article, you need to have a basic understanding of Spring’s annotation-based bean management configuration. If you do not understand the function of the above four classes, you will not be able to talk about learning spring source code. This series of articles is not a basic spring configuration learning article.
-
A basic understanding of jdK8 lambda is required.
3 Source code Analysis
3.1 Source stack frame
First let’s take a look at the main stack frame of the source code to analyze:
In the figure above, each blue block represents a method, and the numeric part indicates the order in which the method is executed (the smaller number is executed first). The large number method between two adjacent methods is the method that the program calls during the execution of the small number method (similar to stack information in debug). Our analysis of the source code will also follow the order “Create all singleton beans”, “Create Chicken object”, “fill Chicken object properties”, “Create Egg object”, “fill Egg object properties”, “get Chicken object” and so on.
3.2 Creating all singleton Beans
Methods 1. Is AnnotationConfigApplicationContext class constructor, constructor initializes the create operation of Bean derivation. 2. One can pay attention to the method to perform the finishBeanFactoryInitialization method, that is, the source code for the stack frame in the figure 3. Methods. Instantiate all Remaining (non-lazy-init) singletons. “// Instantiate all Remaining (non-lazy) singletons. The main purpose of the uncreated singleton is to create the remaining non-lazy-loaded singleton objects. So the two Bean objects we defined, Chiken and Egg, are obviously created in this method, and why the “rest” rather than all, and where the other non-lazy-loaded singletons are created, are not the issues described in this article.
3.3 Creating the Chicken Object
Spring creates a BeanDefinition object for each Bean object before creating it. The BeanDefinition object collects user-defined configuration information about the Bean, such as the type of the Bean object, id and name of the Bean object. The configuration information can be XML configuration files or annotation-based configuration information.
With this information gathered in the form of a BeanDefinition, Spring starts to initialize the non-lazy-loaded singleton (here we’ll just examine the loading of Chicken and Egg, our own Bean objects that are related to the loop dependency). That is, execute the 5.getBean method. Method 5. Is an empty shell method that calls method 6. DoGetBean internally. A method called getSingleton(String beanName, Boolean allowEarlyReference) is executed during the doGetBean method execution. This method is defined as follows:
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 main process is to check whether there is a Bean object with the corresponding name in the map object singletonObjects through the beanName parameter. EarlySingletonObjects does not check whether there is a Bean object with the corresponding name in the map object. ObjectFactory < singletonFactories > < getObject > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > < Bean > Then clear the beanName mapping for the singletonFactories and place the resulting Bean object into earlySingletonObjects. It was also a method isSingletonCurrentlyInCreation (String beanName) Its interior is by looking at a Set object is called singletonsCurrentlyInCreation containing the specified beanName, to judge whether the single example bean is creating bean object.
These three Map objects and a Set object are very important cache in Spring to solve the problem of loop dependency. Let’s refer to “three Map and a Set” for short. The three Map objects are executed in the sequence of beanName corresponding Bean objects. They are referred to as level 1 cache, level 2 cache and level 3 cache respectively.
-
SingletonObjects: Level 1 cache. The Bean objects in this cache are Bean objects that have gone through the full Spring life cycle,
-
EarlySingletonObjects: Level 2 cache. The Bean objects in this cache are Bean objects that have been created but have not gone through spring’s full life cycle.
-
SingletonFactories: Three-level cache. The cache holds the beanName and a factory class ObjectFactory object that gets the Bean object.
Method 6. When doGetBean first calls getSingleton(String beanName), it fails to retrieve the Bean object corresponding to the argument chicken from all three caches. 7. GetSingleton (String beanName, ObjectFactory
3.4 Populating the Properties of the Chicken Object
Following the previous section, in method 11. Spring finds the desired property :Egg object and populates it with its value through the @Autowired annotation in the Chicken class, in method 12. PostProcessProperties, Incidentally the @autowired annotation dependent attribute to processing, from class AutowiredAnnotationBeanPostProcessor @ Resource annotation dependent properties by CommonAnnotationBeanPostProcessor class to handle. The main function of methods 13 to 17 is to find the appropriate beanName so that the Bean can be used to find the corresponding Bean to fill the Egg object in Chicken. This part of the code is not relevant to the main idea of this article, the future article will analyze, interested children can debug the code.
3.5 Creating an Egg Object
Following the previous section, Spring passes the appropriate beanName(egg) it finds through method 17. ResolveCandidate, and performs the egg Bean object retrieval through method 18. GetBean. The method stack called in this section is the same as that in “3.3 Creating a Chicken object”, with the only difference being that the beanName parameter passed in section 3.3 is chicken and the beanName parameter passed in this section is egg.
3.6 Populating Egg object properties
This section is based on the section “3.4 Populating the properties of the Chicken object”. The method stack is the same in the two sections, but the difference is only the parameter difference. Spring discovers that the Egg object needs to inject a Chicken object.
3.7 Obtaining the Chicken Object
The method 31.getBean and method 18. getBean are both analyzed here because we have a Bean object that needs to be injected in our own defined Bean object. However, method 31. Passes chicken, and the chicken object is analyzed in section 3.3. The third level cache singletonFactories in the three Map and Set stores a corresponding ObjectFactory object. Spring uses this ObjectFactory object to get the corresponding Chicken object, avoiding a loop dependency.
3.8 Caching Egg and caching Chicken
In section 3.6 we get the Chicken object, the member variable required by the Egg object. As the method stack frames return layer by layer, we focus on method 20 after method 21. After the program executes method 21. GetObject and obtains the Egg Bean that has gone through the Bean life cycle, the Egg Bean in method 20. To perform two of the more important method in afterSingletonCreation and addSingleton, among them the former three Map a Set of the Set of objects singletonsCurrentlyInCreation egg removed, Indicates that this Bean object is not a Bean object being created and that Bean creation is complete. The latter stores the Egg Bean in the level 1 cache and clears the mapping of the Egg in the level 2 and level 3 caches, so that the Spring life cycle of the Egg Bean is almost complete. The Chicken object also executes afterSingletonCreation and addSingleton methods to complete the Spring life cycle of the Chicken Bean.
3.9 Source code analysis Summary
-
Create chicken object, create Egg object: the steps mainly solve the creation of a Bean raw Bean object and the preparatory work, and the loop dependency of this article is mainly related to the three Map a Set of objects to save the content of the modification.
-
Fill Chicken object properties, filling Chicken object properties: this article mainly through AutowiredAnnotationBeanPostProcessor class complete dependent objects collecting and is suitable for the screening of beanName dependent objects.
-
Get the Chicken object: This is done primarily through the third level cache, avoiding an endless loop of repeated Chicken object creation.
-
Egg created by caching and Chicken created by caching: Complete the cleaning work, and put the Egg Bean and Chicken Bean that have completed the spring life cycle into the level 1 cache for the client program to fetch and use from Spring.
4 Cache data changes
In the chapter of “3 source code analysis”, with the program running process in addition to the method call and method return generated by the thread method stack diagram method pushing and out of the stack. What’s changing behind this in and out is the data in our three maps and one Set.
In the beginning section “3.3 Creating Chicken object” and the end section “3.8 Creating Egg and Chicken after Caching” of source code analysis, we have analyzed the three maps and one Set. However, it is not that only these two sections have data changes, but the principle of their cache changes and the following sections. The only difference is that the method calls take different arguments. The whole data change chart is as follows:
Each state diagram in the figure has a step indicator in the form of “[A, B]”, where A and B respectively represent a method number in the source stack frame diagram in section 3.1, and the parentheses are closed left and open right, which is common in advanced mathematics. Indicates that the state of the cached data in the program from method A to method B (including a and not including B) is consistent with the table below.
Through the observation of eight table data, we can find that for the object mapped by the same beanName, it basically experiences an upgrade process from the third level cache, the second level cache and the first level cache. As for the role of level 3 caching, which is often confused on the Internet, bloggers believe that level 3 caching exists based on the following two facts:
-
Some Bean objects (but not all Bean objects) are referenced by other Bean objects while they are being created.
-
The Bean lifecycle process is a costly one.
In this article, only the Chicken object is referenced by other objects during creation and the Egg object is not. Since the third-level cache stores the subsequent creation of a raw bean, a Chicken object referenced by another object at the time of creation can return the Chicken bean after executing the subsequent processing of the bean object stored in the third-level cache (where AOP’s functionality is implemented). Egg objects that are not referenced during creation simply waste a little memory in the third-level cache and avoid repeating some of Spring’s lifecycle logic on Egg beans, which is likely to be a costly process, such as an AOP implementation.
5 concludes
There is a well-known saying in the programming world that “algorithm plus data structure equals program”. In this article, “3 source analysis” and “4 cache data changes” act as Spring’s solution to the algorithm and data structure in the loop dependent program based on @AutoWired annotated beans, respectively. And the key to understanding it is a deep understanding of the data changes of “three maps and one Set”.
The resources
Spring Circular Dependencies