Drought cavity be out of tune
Bought an impulse game controller last Monday.
When I was a kid, I loved playing games. At that time, gamepad games still had cards, adventure islands and Contra. Later, I came into contact with computer games and rarely played gamepad games.
Before I downloaded a “Ancient Sword 3”, played with the keyboard for a while, may be my hand damage, always feel unable to respond, so I want to buy a handle to see if it can be better.
I recently bought the Witcher 3 on Steam at a discount. My friend said that it’s not the money that’s expensive, it’s the time. Yes, I am very busy after work. My spare time is not enough. I have too little time to play games. This witcher 3 should last me a year
However, to buy the handle is to play a bit more relaxed games, it is necessary to relax properly after work, moderate good.
Today I’m going to bring you a source code analysis article about Spring IoC. In fact, source code analysis is not suitable for writing articles, make video is better, because the code is more, and complex, and call the chain is long, it is difficult to write clearly with pictures and texts, I try to write it clearly
The version used in this article is SpringBoot 2.3.3.RELEASE, and the corresponding Spring version is 5.2.8.RELEASE.
Start from the Debug
In general, source code parsing is divided into several approaches: direct reading of the code; Analyze the relationship between classes and draw UML diagrams; Debug Runs the program.
Sometimes the source code can be complex, such as Spring, which has long links. If it is difficult to look at the code directly, we can start with Debug.
This article is the source code analysis article, so it may be a little laborious to read directly, “strongly recommend to follow the article with Debug!!”
So for this article, I create a new empty SpringBoot project, and then I make a breakpoint on the boot class, and Debug starts:
The run method of the SpringApplication class contains an operation to create the ApplicationContext:
Of course, there is some setting up of the environment and Banner before that. So what is this ApplicationContext? Through the debug window we can see that create a AnnotationConfigServletWebServerApplicationContext instance. Take a look at its class diagram:
ApplicationContext
In the class diagram above, we focus on two of the more important interfaces: BeanFactory and ApplicationContext.
BeanFactory is, as its name suggests, a factory class that provides methods to get beans, “the most basic interface of the IoC container.” The ApplicationContext interface inherits from the BeanFactory and is considered a more advanced container by inheriting more features from other interfaces. We can also see from Debug that Spring creates an ApplicationContext when it starts, and then uses this ApplicationContext to perform subsequent operations on the Bean.
refresh
Continue debugging, and a few lines down you can see that a refresh operation has been performed on the previously created context. When you go in, you discover that it’s actually the refresh() method of the context being called. The implementation of this method is in an abstract class AbstractApplicationContext inside.
The code for this method looks relatively simple and clean, as a series of method calls. These methods are protected and are basically left to subclasses to implement. Here is a typical application of the “template method design pattern”.
Each method is annotated in English to explain what the method is used for, so I won’t translate it here. But we need to focus on the second method (line 533, where I typed the bookmark) and the third method (line 536, where I typed the Debug breakpoint).
The bookmark function of Idea is mentioned here, which is very useful when reading the source code. You can place a marker where you think it’s important so you can easily return to that place at any time later. On Windows, typing a regular bookmark is the F11 key, and if you want to number the bookmark, you can hold down Shift + F11 to give it a number or a letter for special identification. If it’s a number like 3, you can press CTRL + 3 anywhere to jump back to the marked spot.
Registered Bean
Registering beans is done through the second method in refresh’s series of methods mentioned above:
invokeBeanFactoryPostProcessors(beanFactory);
Copy the code
The Debug, found that using a static methods invokeBeanFactoryPostProcessors PostProcessorRegistrationDelegate class. The code for this method is long, so let’s get right to the point:
Inside the ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry method, through the final line method in processConfigBeanDefinitions method.
The purpose of this method is to find the entry to the configuration class. Since I’m using SpringBoot directly, the configuration class entry only has our own defined boot class, SpringBaseApplication.
Inside this method you can find a line like this:
parser.parse(candidates);
Copy the code
Note that the Candidates here are our Configuration class.
Debug to see if the candidate is a Bean defined with annotations. The @Configuration annotation itself is decorated by the @Component annotation, so it is an annotated Bean.
There is a BeanDefinition class, “which is the data structure Spring uses to wrap and decorate a Bean,” including the Bean’s dependencies, scope, and much more.
We continue to Debug, came to the ConfigurationClassParser processConfigurationClass method of a class. Also go straight to the most critical code:
And then you go in and you find another method that has a lot of code. Don’t worry, we’re still looking at the most critical code:
As you can see, the @ComponentScan and @ComponentScans annotations are used to specify which package beans Spring should scan on the configuration class. This is where Spring is taken out. Once retrieved, Debug on line 294 to see how it resolves:
Internally, this method is simple: find basePackage and scan. If we define basePackage in @ComponantScan, it uses what we defined. Those of you who have experience with SpringBoot may know that “if we don’t define basePackage, Spring by default scans beans under the boot class package and its subpackages.” How does this work? It turns out that @SpringBootApplication is modified by @ComponentScan, and it doesn’t define basePackage, so Spring, at line 123, reflects the package that started the class and adds it to the basePackage.
We then proceed to the doScan method through line 132.
Inside of this method is the basePackage passed in, scanning the class to get the corresponding BeanDefinition. We then loop through each BeanDefinition and unregister the Bean by calling the registerBeanDefinition method.
We found from registerBeanDefinition method, finally reached the DefaultListableBeanFactory registerBeanDefinition method of a class. In this method, Spring will first try to fetch a beanDefinitionMap from this.beanDefinitionMap via beanName, or if it doesn’t have one, put it into the map. At this point, the Spring process of registering beans is complete.
Initialize the Bean
After registering the Bean, you just hand it over to Spring for management, but at this point the Bean is not initialized. We went back to the first AbstractApplicationContext the place of a series of template method in a class. The next method is to initialize the Bean.
registerBeanPostProcessors(beanFactory);
Copy the code
By convention, the Debug all the way into, in PostProcessorRegistrationDelegate registerBeanPostProcessors method of a class, you can see here for our definition of beans, A beanFactory.getBean operation is performed. This operation is an attempt to take a Bean from Spring. If the Bean is not already initialized, Spring does the initialization and dependency injection.
The main logic is implemented in the AbstractBeanFactory class’s doGetBean method. At this time, our user-defined Bean has not been initialized, so we will go through the initialization process. Continue debugging and you will see that this is done through the doCreateBean method.
In doCreateBean, createBeanInstance is used to create the Bean, and if a dependency is found, the dependency is handled through the populateBean method.
During Bean creation, you try to instantiate the Bean using factory methods, constructors, and reflection.
During dependency processing, if a dependency is found, the getBean method is called through the beanName of the dependency, thus creating a “recursive call” (if the dependency has other dependencies). Finally, the properties of the Bean are parsed through the applyPropertyValues method, and the corresponding dependencies are injected.
In the case of property injection, the underlying method is BeanWapperImpl’s setValue method, which is implemented based on reflection.
At this point, Spring completes the process of scanning, registering, and instantiating the Bean. You can then use the ApplicationContext to get the Bean instance.
Of course, Spring’s IoC does a very good job, the Bean life cycle and extension, how to solve the cycle dependency, and so on, are the corresponding code to achieve. This is just the main thread from Spring startup to Bean instantiation. If you are interested in more details, you can Debug the other branches yourself.
Source code parsing techniques
Finished writing the article, feel the source code analysis class article or pretty difficult to write, the main oneself have to manage a complete process or more time. Here are some tips for parsing source code.
Debug
Debug is very useful because it visually retrieves a lot of runtime information. For example, Spring’s design is very complex, with many design patterns, many interfaces and inheritance relationships. If you don’t Debug and just look at the code, you can’t see what the implementation class of this place is intuitively. If you want to click into it, you will find there are more than ten implementation classes, and suddenly you are confused.
For Debug, memorize the shortcut keys skillfully. Here are two useful functions that are not commonly used in Idea.
One is F9, which allows you to jump directly to the next breakpoint. Use Alt + F9 to jump directly to the line where the cursor is.
Another right click on a breakpoint to set conditions for the breakpoint. This is very useful in loops where you can jump right to where you want your condition to be.
bookmark
Bookmark marks code. When we read the source code, it’s easy to jump over it. If you don’t mark it, you may lose it very quickly. Using tags can help us remember important code and jump quickly.
The class diagram
Right-click on a class and choose Diagrams -> Show Diagram to view the Diagrams. Useful for teasing out relationships between classes in source code. Classes can also be added and removed from the diagram to customize the class relationships we care about.
Don’t get bogged down in details
Getting into details is a very easy mistake to encounter when reading source code. A lot of people feel that it is difficult to read the source code, do not understand, may be too involved in the details. In fact, we do not have to study each line of source code to understand, as long as we understand its main implementation logic on the line, for we are interested in, can go further in the back to see, so that there is a macro perspective, in order to clarify the whole design ideas, the ins and outs.
About the author
I’m Yasin, a good-looking and interesting programmer.
Wechat public number: made up a process
Personal website: https://yasinshaw.com
Pay attention to my public number, grow up with me ~