Interviewer: Tell me about the startup process of Springboot (50,000 words to analyze the startup process)

Article Directory

foreword

After writing the preface, I paused for thousands of milliseconds, and suddenly I didn’t know what to say. The original intention of writing this blog was indeed the interview question that the interviewer once asked me. After reading the source code, no, I will make up the lesson. I am really busy at work, but this is not an excuse for me not to study. The summary is that I find that my inherent thinking still lacks the spirit of inquiring. In the future, I will develop a habit of asking why, remember!

1. What is Springboot

I decided to get to the bottom of it and go shopping on the official website~
I personally think that the best way to learn a technology and want to know more about it is the official website. If you don’t know anything about it, you can read what others wrote first. , After all, first look at some translations of the official website into words that are easy for Xiaobai to understand, which can eliminate our fear of learning new technologies (why should we be afraid, because we are afraid that we will not understand, feel stupid, and lose confidence).
It is said that springboot has been born for a long time. I really did not learn in a down-to-earth manner. Instead, many things stay in a place where I have only a little knowledge and a little taste. I will change this habit in the future. Remember!

Official website: https://spring.io/projects/spring-boot
In addition, you can also enter from Projects in https://spring.io :

insert image description here

insert image description here
There is no introduction to the Chinese version, so translate it:
OVERVIEW ( Overview )
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.

Spring Boot makes it easy to create self-contained, production-grade Spring-based applications that you can "one-click to run".

We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.

We take a firm view on the Spring platform and third-party libraries, so you can get started with little effort. Most Spring Boot applications require minimal Spring configuration.

If you’re looking for information about a specific version, or instructions about how to upgrade from an earlier release, check out the project release notes section on our wiki.

If you're looking for information on a specific release, or instructions on how to upgrade from an earlier release, check out the project's Release Notes section on our wiki.

Features特点
Create stand-alone Spring applications

Create a standalone Spring application.

Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)

Directly embed Tomcat, Jetty or Undertow (no need to deploy war files)

Provide opinionated ‘starter’ dependencies to simplify your build configuration

Provides "starter" dependencies to simplify build configuration

Automatically configure Spring and 3rd party libraries whenever possible

Automatically configure Spring and third-party libraries as much as possible

Provide production-ready features such as metrics, health checks, and externalized configuration

Provides production-ready features such as metrics, health checks, and externalized configuration

Absolutely no code generation and no requirement for XML configuration

Absolutely no code generation and no XML configuration required.

The process of translation is also the process of learning English. I really don’t know how to translate some words in it, so I used a dictionary.
Here I will learn English by the way. Although I digressed, I did not digress on the way of learning.

stand-alone independent
production-grade production-grade
just run one-click operation (paraphrased here)
take an opinionated view of something hold a firm view on some things
you can get started with minimum fuss you can start effortlessly Use
3rd party libraries
whenever possible
production-ready features production-ready features

Summary: SpringBoot can quickly build a Spring project to help developers focus more on business logic development.

2. Startup process

2.1 Build Spring Boot project

I won’t write about the creation process. I originally wanted to recreate a Spring boot by myself, but after I reinstalled the system, I could only install the community version of the idea first. There is no quick way to create it, but the springboot official website provides a way ( https ://start.spring.io ), just import it into idea after creation.
insert image description here
The place where the red box is drawn is the place I changed. After writing, click GENERATE at the bottom, and it will be downloaded automatically after the creation is completed.
After using the idea to open it, modify the address of the maven warehouse, wait for him to download and check the dependencies, the structure is as follows:
insert image description here
Of course, there are some useless files, which can be deleted or kept, which is not my focus this time.

2.2 Startup log

The log printed by direct run startup is as follows:
insert image description here
Seeing this familiar log, I have never thought about it before:
where is the jdk version of the first line printed?
Where is this spring logo printed?
Where is Tomcat loaded when it is initialized?
Where is the Servlet engine loaded at the beginning?
Where is the web application context loaded?
Where is the last successful startup loaded?
. . . . . .
Wait, let's start our treasure hunt and reveal the secrets one by one! ! !

2.3 Startup process

After reading it, I feel that it is divided into two parts: the constructor of SpringApplication and the operation of the run method.

analysis description

  • I really don’t know if I don’t see it. When I look at the next jump, I didn’t think it was just a simple startup. In fact, there are too many things involved, and the main method I saw is really just the tip of the iceberg. Let’s finish writing this article with 53,000 words (of course, there are many method names that occupy the number of words), so the analysis process is very long-winded and involves a lot of detailed implementation (of course there are many things that have not been mentioned), mainly because I am the first To analyze the Springboot source code once, I want to clarify and record these processes during the analysis, so that I can sort out the knowledge system inside. The article is quite long. After all, I finished this blog after reading and writing it for more than a month, so I can read it several times when I read it. If you feel long-winded, you can directly read the title and jump to the part you want to read.
  • Of course, for the purpose of learning rather than interviewing, if you have enough time and enough interest, it is strongly recommended that you follow the code execution process several times, because the eyes understand, but the hands do not move, and it will still be like a passing cloud in the end. The purpose of our looking at the source code, I feel that we can understand the ideas and excellent writing methods in it, and apply it to our own projects later to better serve the business.
  • In addition, the analysis in this article is based on the analysis of the source code and corresponding comments. I still haven’t delved too deeply into the reasons for most of the implementations. I will take a look at it later when I have time and delve into the reasons for this design. Of course, the process I read I also wrote about the places I don’t understand, and the students who know it hope to enlighten me~
  • How to eat this article: The whole startup process is really a lot, and it is eaten multiple times; because many places are connected, I also mentioned in the article where a certain code was analyzed. If you forget it, you can look back. At this time, you can Just press Ctrl+F to search.

2.3.1 Part 1: The constructor of SpringApplication

The startup entry of Springboot: the run method in the SpringBootDemoApplication.main method:
insert image description here
click the run method in mian to enter the run method of the SpringApplication class
insert image description here
Here, a space is opened up in the heap to store the created SpringApplicationinstance objects, and SpringBootDemoApplication.classthe existing as SpringApplicationthe structure The input parameters of the function, and the args in the main method are used as the input parameters of the run method. Then we enter SpringApplicationthe constructor.
Start by debugging to see what the values ​​of each attribute are after loading, as shown in Figure 1 below:
insert image description here
Let's focus on a few attributes inside:

A. webApplicationType (web application type)

Enter the WebApplicationType.deduceFromClasspath() method,
insert image description here
you can see it through Debug, here is the last line directly: return WebApplicationType.SERVLET, that is to say, webApplicationType=SERVLET, as shown in Figure 1 above;

B. Boot registration initializer

I don't know how to say this name, but seeing the last two words, it should be a registration initializer. The next two setXX methods also have a loading initializer. You can see that they both call a common method, but the input parameters are different getSpringFactoriesInstances.
insert image description here
Enter getSpringFactoriesInstancesthe method, and finally SpringFactoriesLoader.loadFactoryNamescall the method SpringFactoriesLoader.loadSpringFactories. There is a line in this method.
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
You can see through debug that the loaded URL is
1) spring-beans/5.3.23/spring-beans-5.3.23.jar!/META-INF/spring.factories
2) spring-boot-devtools/2.7.5/spring-boot-devtools-2.7.5.jar!/META-INF/spring.factories
3) spring-boot/2.7.5/spring-boot-2.7.5.jar!/META-INF/spring.factories
4) under the springframework in the maven package spring-boot-autoconfigure/2.7.5/spring-boot-autoconfigure-2.7.5.jar!/META-INF/spring.factories
. In fact, it should be loaded under all packages. META-INF/spring.factoriesfile, as long as there is BootStrapRegistryInitializera class with key=, it will be loaded.
Next, I will use debug to show that
insert image description here
there are 14 properties in the screenshots of the content loaded in the 3 and 4 packages. These properties are in the read file, but there are far more than 14 in this file. A closer look shows that 14 are taken. The first one is placed in Properties. If you don’t believe me, you can also count it. After loading the key-values ​​of the files in the 4 jar packages
insert image description here
and putting them in the result, it’s time to replace the factory implementation in the map. Name (factoryImplementationName is a literal translation), the structure of result is as follows: Then execute the lambda expression inside the method, and put the listener or initializer installed in the ArrayList into the container. When replacing, I found out why the length of implementations is 31. I counted the files in spring-boot and spring-boot-autoconfigurespring-boot-autoconfigure/2.7.5/spring-boot-autoconfigure-2.7.5.jar!/META-INF/spring.factories
insert image description here
spring.factories
Map<String, List<String>> result = new HashMap();
insert image description here
Collections$UnmodifiableRandomAccessList
insert image description here
META-INF/spring.factoriesFailureAnalyzerThere are 31 implementation classes in total.

I also read other things and found that the number is not enough. In the process of debugging, I can see that other springframework dependencies are also loaded. For example,
org.springframework.context.ApplicationContextInitializerthere are 8 of them. I found that only the files in spring-boot and spring-boot-autoconfigure were META-INF/spring.factoriesfound. 7, I still org.springframework.boot.devtools.restart.RestartScopeInitializerhaven’t found it, so I remembered to load it spring-boot-devtools/2.7.5/spring-boot-devtools-2.7.5.jar!/META-INF/spring.factories, and I found it here:
insert image description here
After the replacement, I saw the Map result displayed on the debug console:
insert image description here
After the replacement, I found that
insert image description here
it was only changed, why should I replace it here? , I didn’t see it very clearly, let’s look down first to find SpringFactoriesLoader.loadFactoryNamesthe method of calling this method: we couldn’t find it
insert image description here
in the 4 jar packages , so the property of the constructor of SpringApplication is an empty ArrayList./META-INF/spring.factoriesBootstrapRegistryInitializerthis.bootstrapRegistryInitializers

C. Set the initializer

Let's go back to the constructor of SpringApplication and look at the methods of initializers and listeners:

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

I also saw the familiar method, getSpringFactoriesInstancesthat is, the content under the 4 jar packages read by the above-mentioned key analysis /META-INF/spring.factories. We found that there are ApplicationContextInitializersome , and it has 8 children. This has also been analyzed before, so I won’t go into details here. The screenshot shows the content of debug:
insert image description here

D. Set up the listener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

But ApplicationListenerthere are also 10 children, and I won't look for them again where they are in the jar package.
insert image description here

E. this.mainApplicationClass (set application class)

The last step is to infer the main application class.
insert image description here
At this point, the execution process of the constructor is finally analyzed. I really don’t know if I don’t analyze it, and I’m startled when I analyze it. Of course, this is just my own analysis. There are still 2 points in it that I haven’t figured out. I still need to debug a few more times before I can fully understand it. If you really understand it, you won’t forget it for a long time. Only in this way can we internalize it into our own knowledge.
In addition, a mechanism is used here, which is also the Dubbo SPI mechanism mentioned in a blog I wrote before . It is the first to be used in java. Dubbo has been optimized on the basis of java's SPI mechanism.

2.3.2 The second part: the operation of the run method

First look at what is written in this method.
insert image description here
In order to facilitate analysis and understanding, I will use the printed banner information as the dividing point to analyze and see what has been done before and after.

A. Code implementation before printing banner

//1、获取系统纳秒数时间,用于统计执行的总时间
long startTime = System.nanoTime();
//2、创建启动的上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//3、配置无领导的属性
configureHeadlessProperty();
//4、获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//5、发布启动的事件并由对应的监听器进行处理
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
    
    
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	//6、准备环境变量
	ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
	//7、配置忽略的Bean信息
	configureIgnoreBeanInfo(environment);
	//8、打印Banner信息
	Banner printedBanner = printBanner(environment);

Let me first write the general process in the comments, and then I will talk about other operations except 1.

A.2. Create a startup context

insert image description here
Here, bootstrapRegistryInitializersthe pair has been traversed. Obviously, there is no data in it. It has been mentioned in detail in 2.3.1.B here, so I won’t repeat it here. The context of the final startup is DefaultBootstrapContextthat this function is used in step 5.

A.3. Configure the attributes of no leader

In fact, I don’t understand this very well. What is the purpose of setting this, but I still bite the bullet and look down at the attribute values
insert image description here
​​​​obtained from System java.awt.headless, and put the results in. It seems to be like this. Partners can also be confused when they see this~
In Properties.getPropertythe method, true is returned.
insert image description here
I was curious about what this java.awt.headlessis, it looked like a path name, so I found the awt package, and in this package I only saw HeadlessExceptionthe class:
insert image description here
there is a comment on the class:

Raised when code that relies on a keyboard, display, or mouse is called in an environment that does not support it.

What is the purpose of setting this value to true? I saw the correct explanation from an article
: SpringBoot actually wants to set the application to allow it to start even if the display is not detected. For the server, There is no need for a display, so set it like this.
Thanks to this great god for his explanation, I am so enlightened~

A.4. Get the running listener
//4、获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);

It's a familiar recipe and a familiar taste again, and getSpringFactoriesInstancesthe method is here again.
insert image description here
We can easily spring-boot/2.7.5/spring-boot-2.7.5.jar!/META-INF/spring.factoriesfind SpringApplicationRunListenerthe subclass in it, org.springframework.boot.context.event.EventPublishingRunListener
insert image description here
so getSpringFactoriesInstancesthe listener returned by the method is also EventPublishingRunListener, but getRunListenersthe return in the method returns a new created one SpringApplicationRunListeners, constructs this class, and puts the listener in the constructor.
insert image description here

A.5. Publish the start event and handle it by the application start listener

Then we go down and call SpringApplicationRunListeners.startingthe method.
insert image description here
Entering the starting method calls doWithListenersthe method again. The first parameter in this method is the step name, the second is a functional interface, and the third is the step, the most important of which is the functional interface.
insert image description here
Entering doWithListenersthe method, the method body this.applicationStartup.start(stepName);finally returns a DefaultStartupStep.
doWithListenersMethods SpringApplicationRunListenerswill be used repeatedly in the class, which will be mentioned later.
insert image description here
The method is called later this.listeners.forEach(listenerAction);, so here listenerAction is the result returned from the method doWithListenersin the method (listener) -> listener.starting(bootstrapContext), and this method is the core method. Due to insufficient internal strength training, I didn’t understand this method at first, so I repeatedly debugged it 5 or 6 times before I understood it. The point that I didn’t understand was that there were 10 elements in the Listener collection. Why were there only 10 elements left after filtering? In the picture below, the serial numbers I underlined are 0, 3, 4, and 5, and the remaining 6 are filtered out.
insert image description here
I found the creation information of these four classes
insert image description here
and the creation information of other classes is as follows:
insert image description here
I will not post the DevToolsLogFactory with the serial number 7, and the corresponding monitoring class is inside this class, so I will not consider it.
The more critical method is startingthe method. In SpringApplicationRunListenerthe class, there is a line of comments on this method. Turn it over:

Called immediately when the run method is first started, it can be used for early initialization.

insert image description hereAfter clicking multicastEventin, the most important thing should be getApplicationListeners(event, type)the method. This method returns the 4 monitoring classes mentioned above, and the method is used to invokeListenercall the method doInvokeListener. Let's look at this method last.
Remember this multicastEvent method, which will be used in several places later
insert image description here
The method here getApplicationListeners(event, type)is very important, and the method is finally called retrieveApplicationListeners. There is a method in the loop in this method, supportsEvent(listener, eventType, sourceType)which is the method to filter out these 4 listeners.
insert image description here
When supportsEvent(listener, eventType, sourceType)true is returned, the listener will be added to the collection. This method uses the instanceof keyword to determine whether the previous object belongs to the latter class or its subclass. What does this method mean? There is a line comment on the method:

Determines whether the given listener supports the given event

insert image description here
It can be seen that among the four classes, only other listeners LoggingApplicationListenerare implemented and need to be used for judgment. The last line is connected by &&, and the latter one directly returns True, so let’s look at the previous method. After this method enters the class, this class is implemented , and there is a construction method: this class rewrites method method 10 Most of the classes are the last else directly, that is, the method behind "||". In this method, there is a key point to decide which of the 10 classes will be filtered out. The code is a bit If there are too many, I won’t post too many. If you are interested, you should debug it yourself to learn more. At 2/3 of the method, most classes are filtered out because they return false. The description of this method is: Checks whether the type on the right is assignable to the type on the left. (Explanation: I probably understand this method as whether the generic type of the class implemented when these 10 classes are created is a class or its subclass. I don’t understand it very well. I just explain what I see here. As for the implementation Thoughts, I may need to have more internal skills to understand. In addition, the steps of obtaining the monitor are also the places where I have read the longest time and have the most points that I don’t understand, so I will go here first, and of course I will go further There are places that need to be dug deeper, and interested friends can dig deeper by themselves) Finally, let’s go back to the method to look at the method of calling the listener and call it again : these four classes have re-implemented the method, of course There are 37 classes that write this method, and I won’t list them with screenshots. If you don’t believe me, you can search for it by yourself~ After receiving the event, different implementation classes will process each word according to the event data. According to the aboveGenericApplicationListenerGenericApplicationListenerAdapter.supportsEventType(eventType)
smartListener.supportsEventType(eventType)GenericApplicationListenerAdapterGenericApplicationListener
insert image description here
supportsEventType
insert image description here
this.declaredEventType.isAssignableFrom(eventType))
isAssignableFromClassUtils.isAssignable(ourResolved, otherResolved)
insert image description here
ApplicationEvent
invokeListenerinvokeListenerdoInvokeListener
insert image description here
onApplicationEvent
multicastEventAfter entering, getApplicationListeners(event, type)the returned listener enters the implementation method of the corresponding listener. For example, in the startingmethod here, the passed event class is ApplicationStartingEvent, so which listener class will handle this event, let’s search globally, or click on onApplicationEventthe method In the implementation, breakpoints are set in the four we mentioned above, and you will know which listener handles the event when you see which one has entered. I've tried both methods, and they both work fine. We found the listener to RestartApplicationListenerhandle ApplicationStartingEventthe event, as shown in the figure below, the name of the picture is the start event code picture .
insert image description here
If the event is processed, a line of log will be printed on the console, that is, the line with the red line in the figure below.
insert image description here
So where is this line of log printed? Let me show the calling path simply and crudely. You can check it yourself, or search the log keyword globally Created RestartClassLoaderand then push it backwards. Start from the line that draws the blue shadow in the startup event code diagram
onApplicationStartingEvent((ApplicationStartingEvent) event); : -> Restarter.initialize(args, false, restartInitializer, restartOnInitialize);-> localInstance.initialize(restartOnInitialize);-> - immediateRestart();> -> start(FailureHandler.NONE);-> Throwable error = doStart();-> ClassLoader classLoader = new RestartClassLoader(this.applicationClassLoader, urls, updatedFiles);
It is RestartClassLoaderprinted in the constructor
(secretly tell you: RestartApplicationListenerthere are several events in it, have you found it? We will meet again~)

A.6. Prepare environment variables

After talking about what I thought was the most difficult part, I felt a lot easier.
We're here now and here
insert image description here
prepareEnvironment's how:

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    
    
		// 1、获取或创建环境变量
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//2、配置环境变量
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//3、配置属性源和环境变量的依附
		ConfigurationPropertySources.attach(environment);
		//4、监听环境变量
		listeners.environmentPrepared(bootstrapContext, environment);
		//5、将默认的环境变量移动到最后
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		//6、将环境绑定到SpringApplication
		bindToSpringApplication(environment);
		//7、转换环境为标准的环境变量
		if (!this.isCustomEnvironment) {
    
    
			EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
			environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		//8、配置属性源和环境变量的依附(同第三步,不赘述了)
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
A.6.1 Get or create environment variables

insert image description here
ApplicationServletEnvironmentHere, an application service environment variable class is created . This class inherits from StandardServletEnvironment, this class inherits from StandardEnvironment, and this class inherits from AbstractEnvironment. This class is an abstract environment variable class. In this abstract environment variable class, there is a construction method:
insert image description here
enter In customizePropertySources(propertySources);the method, two more elements are added to the method. After the addition, the environment is as follows: the source of the four elements is also a List, and some of the classes define that this collection is a copy-on-write collection class. Let's take a look at what is in the soure with inde 2 and 3. Here, index=2 is used as an example, which stores system attributes. As can be seen from the figure below, the source stores the basic information of the system, the virtual machine version, and the class version. , operating environment, project directory, etc. The words 3 store the system environment variables, and the keys inside are all uppercase, because we usually configure environment variables in uppercase. Explanation: I won’t expand on the method of obtaining system properties ( ) and system environment variables ( ) here, otherwise it feels endless.
insert image description here
super.customizePropertySources(propertySources);
insert image description here
propertySourceListpublic class MutablePropertySources implements PropertySourcespropertySourceListprivate final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();

insert image description here
insert image description here
getSystemProperties()getSystemEnvironment()

A.6.2 Configuring Environment Variables

Then enter the method of configuring environment variables: configureEnvironment(environment, applicationArguments.getSourceArgs());
insert image description here
Then enter configurePropertySources(environment, args);the method, if the judgment fails, return directly.
insert image description here

A.6.3 Dependency on configuring property sources and environment variables

Going further down is the method of matching property sources and environment variables: ConfigurationPropertySources.attach(environment);after executing this method, propertySourceListthere will be another family member in the collection:
insert image description here
when you follow the steps below:

  • Open propertySourceListthe element with index=0 in the collection;
  • open source;
  • Open sourcethe inside again sources;

Repeat the above three steps continuously, repeating this more than ten times, just like infinite nesting dolls, endless, it really seems to be endless, as shown in the picture below. How is
insert image description here
this effect achieved, and why is it designed this way? To be honest, I really don't know the reason for this design, but we can look at the process of achieving this effect. Enter the ConfigurationPropertySources.attach(environment);method, which is a static method.
insert image description here
The operation here is to pack the 4 attribute source elements in the previous collection as a whole and put them into the newly created object, and ConfigurationPropertySourcesPropertySourcethen put the last line in the first element position in the original sources, which is what we said earlier See the effect of infinite nesting dolls.
insert image description here
If you continue to expand according to the three steps mentioned above, you will have the same endless effect. If there are friends who can see this, and know why there is such an infinite set of dolls, please leave a message to advise~

A.6.4 Monitoring environment preparation

Then comes listeners.environmentPrepared(bootstrapContext, environment);the method, which reuses the method we analyzed above to obtain the listener: the
insert image description here
rewritten multicastEventmethod in the listener is called to publish the event of environment preparation
It's just a monitoring operation
, so what does the listener that receives this event do? The listener to receive this event is EnvironmentPostProcessorApplicationListener, and this kind of no-argument constructor has done another thing we are very familiar with: we will know if
insert image description here
we see it, we have analyzed it before, it is the subclass loaded from it, After loading, these are the ones in the figure below: the method of drawing the red line in the above figure is to convert the class of the string obtained from it into a real class, and traverse each class. These classes rewrite the method and process the environment respectively , when traversing : this method has only one line of code, and a static method is called: in this method, the resource class named is added to it, that is , the collection. When the class is traversed, the attribute resource is also placed in the collection Middle: So far, there are 6 attribute resources in the collection: After adding, there are 7 elements in the collection:SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));spring.factoriesEnvironmentPostProcessor
insert image description here
spring.factoriespostProcessEnvironmentRandomValuePropertySourceEnvironmentPostProcessor
insert image description here
addToEnvironment
insert image description here
randomRandomValuePropertySourceMutablePropertySourcespropertySourceListDevToolsPropertyDefaultsPostProcessor
insert image description here

insert image description here
devtoolspropertySourceList
insert image description here

A.6.5 Move default environment variables to the end

The location of this method is as shown in the figure below:
insert image description here
enter the method and finally return false.
insert image description here

A.6.6 Binding the environment to a SpringApplication

insert image description here
The places marked 1 and 2 in the picture are the places that I am more confused. I didn’t understand the one marked 1, and I asserted something, so I let this line go.
Let’s look at the place marked 2. I have debugged this method no less than ten times, all the way into the content of the method. I don’t know where I did it and kept looping because I didn’t find the loop body. The main loop is as shown in the figure below The bind method in the. If someone who knows can give me some advice, I would like to know where I am stuck?
insert image description here
Where does the ConfigurationPropertyName here come from? In the continuous loop process, I found that some keys are the names in the Json file in the figure below, but I don’t know when this file was read.
insert image description here
I'll add it when I figure it out.

A.6.7 Converting the environment to standard environment variables

insert image description here
Then I performed the third step, configuring the attribute source and the attachment of the environment variable ( ConfigurationPropertySources.attach(environment);)
, I still didn't understand why I had to execute it again, and finally environmentreturned the processed one.

PS: There are indeed a lot in it, I still don't understand it, but it doesn't matter, you can know the general logic first, and then take a look at it later. Maybe it will suddenly become clear.

A.7. Bean information ignored by configuration

insert image description here
It can be seen from the implementation of this method that he is configured to ignore BeanInfo, first check whether the value can be obtained from the properties in System spring.beaninfo.ignore, if not, get it from the environment variable, and set this value in System to True, I don’t understand why this is done here, what is the purpose of setting it to true, why should BeanInfo be ignored, I think it should be used later.

A.8. Print Banner information

In the method in SpringApplication , the bannerMode defaults to CONSOLE, enter the method of the method printBannerin this method , and then call the printBanner method of the SpringBootBanner method. In the class: Of course, you can also customize the picture or text, and put the custom picture or text in the resources folder. From the following code, it can be seen that it supports custom extensions: so far, the first part of the run method The analysis is over. It really took my life, and it took a lot of time, but I still don't understand why.SpringApplicationBannerPrinterprint
insert image description here
SpringBootBanner
insert image description here

insert image description here

B. Code implementation after printing banner

insert image description here
There are roughly 9 steps here, let's take a look at them one by one:

1. Create the context of the application
2. Prepare the context
3. Update the context
4. Post-processing of the update context
5. Print how long the application took to start and complete
6. Publish the event of the start of the context
7. Call the runner
8. Process the capture 9. Publish
ready events

B.1. Create application context

createApplicationContext();The annotation for the method is:

Strategy method for creating the ApplicationContext. By default, this method will honor any previous explicitly set application context class or factory and fall back to a suitable default.

ApplicationContextFactoryEntering this method, the abstract create method is called , so which implementation class's create method should we look at?
insert image description here
We entered it and ApplicationContextFactory.DEFAULTsaw that there is a loadFactories method in the for loop.
insert image description here
When you follow SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())the method all the way down, you can see the SPI in the queried file again , so you can really find its interface implementation META-INF/spring.factoriesby searching the full text . ApplicationContextFactoryThat is the part of my red box in the picture below.
insert image description here
Then you know which implementation class to go to see the created method.
insert image description here
So, the final contexttype is AnnotationConfigServletWebServerApplicationContext. Then let's take a look at the relationship between
this class and the class returned by the create method. Let's name this picture a context relationship inheritance diagram . From this inheritance diagram, we can see that the top ( ) and the bottom of the red box are drawn The following ( ) relationship, this figure we will also use a method in the preparation context in the next section, let’s look familiar first, then what’s in this, let’s take a look at the constructor: and what is this, let’s take a look Constructor: There is actually a registration method in the last line. What is registered here? Let’s skip here and do further analysis later.ConfigurableApplicationContext
insert image description here
ConfigurableApplicationContextAnnotationConfigServletWebServerApplicationContextAnnotationConfigServletWebServerApplicationContext
insert image description here
AnnotatedBeanDefinitionReader
insert image description here

B.2. Prepare context

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
Let me talk about the method of preparing the previous line of the context: context.setApplicationStartup(this.applicationStartup);
insert image description here
look at ApplicationStartupthe translated Chinese comments of the class:

Use StartupStep to detect application startup phases. The core container and its infrastructure components can use ApplicationStartupMarker to mark steps during application startup and collect data about the execution context or its processing time.

setApplicationStartupAfter clicking on the method, you can see the notes:

Allows the application context to record metrics during runtime.

After going in, it is the method of setting attributes, which will not be repeated here. Then let's take a look at the key method:
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
this method has the most parameters in the started process. It needs to be explained first that there are more processes in this method and the following method of updating the context, so you need to be mentally prepared.
Let's look at this method in several parts. It is still a convention. Unless it is very simple, we will not analyze it, and the others will be analyzed one by one:

B.2.1 Processing context

postProcessApplicationContext(context);The Chinese interpretation of the method:

This method should be used for any related ApplicationContextprocessing. Subclasses can do additional processing as needed.

insert image description here
It is still debugged many times, and I have not figured out how to translate this method. There are 3 if blocks in the method, but only the setConversionServicemethod in the last if block goes on. I didn’t understand at first, why do I need to set one like this? Since there is a set, there must be a get. Where is the get used and what conversionServiceis it used for?
Enter setConversionServicethe method to see a note:

Specifies the Spring 3.0 conversion service used to convert property values, as an alternative to the JavaBeans property editor.

The comment says that this started from Spring 3.0, which is equivalent to an upgrade.
Let's context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());break it down,
first look at context.getEnvironment().getConversionService())the method:
click on getConversionServicethe method, we enter ConfigurablePropertyResolverthe class, there is a comment and usage method on the method:
insert image description here
enter the implementation class of this method AbstractPropertyResolver:
insert image description here
finally return a ConfigurableConversionServiceclass, and feel the relationship between each class at this time It's a bit complicated, but it's clear when we look at the relationship diagram: In the above diagram, we took
insert image description here
it from the inside , this inheritance , , of these two interfaces, the former is processing conversion ( ), and the latter is converter registration ( method) .ConfigurablePropertyResolverConfigurableConversionServiceConfigurableConversionServiceConversionServiceConverterRegistry<T> T convert(@Nullable Object source, Class<T> targetType);void addConverter(Converter<?, ?> converter);

Let’s talk about context.getBeanFactory().setConversionService(ConversionService)the method again, click setConversionServiceto enter AbstractBeanFactoryit, and see the set and get methods, so where is the get method called?
insert image description here
As shown in the figure below, you can see which classes have called getConversionServicemethods:
insert image description here
From the third call of typeConverter in the figure above, it can be seen that it is related to type conversion. We saw it in the relationship diagram of the previous class, and Conversion means conversion. This is the ConversionServicetop-level interface of the conversion service, and there are several implementation classes below it:
insert image description here
I went into StandardBeanExpressionResolverthe class, and you can see it from the name , this class is used to parse beans, and the annotations on this class also have similar instructions:

All beans containing {@code BeanFactory} are provided as predefined variables with their generic bean names, including standard context beans such as "environment", "systemProperties" and "systemEnvironment".

I then followed up and found that the meaning of the naming of some methods is probably related to the analysis of Spring's xml configuration. When using Spring in the early stage, we generally use xml configuration, which will write some String strings, then these strings How is it converted into the corresponding class? I think it is related to this interface. Look at the picture below to understand, including Integer,Double,Booleanthe conversion of basic types, date conversion, etc., each of which has a corresponding interface implementation:
insert image description here
In fact, there are still things that can be said to be dug, but due to the limited space, just click here , leave a homework here: use ConversionService to convert the number of String type to Integer.


After talking about conversionService, let’s talk about the first if block. After judging that it beanNameGeneratoris not empty, it will be put into the cache. When will it be set? There is a method
in , this method is called , but where this method is called, you can’t find it ~ and the following if block is not null, and the following logic is being performed, then where is this set Yes, it is found in the constructor we analyzed in the first part, but this is indeed null by default, which means that there is a set in other places. The same as called in it: there is a method in , this method is called , but where is this method called, I can't find it. I will add it after I understand it here. Of course, if you see this and know why, you still hope to leave footprints and solve my doubts for me~SpringApplicationsetBeanNameGeneratorSpringApplicationBuilderbeanNameGeneratorbeanNameGenerator
insert image description here
resourceLoaderresourceLoader
insert image description here
beanNameGeneratorSpringApplicationBuilder
insert image description here
SpringApplicationsetResourceLoaderSpringApplicationBuilderresourceLoaderresourceLoader

B.2.2 Initializers are applied to the context

applyInitializers(context);
Chinese comment for this method:

Apply whatever is applied to the context before the context is ApplicationContextInitializerrefreshed.

After entering this applyInitializersmethod, it is a for loop. What is the content of the loop? getInitializers()The returned result is the 8 initializers in C and the method of setting the initializer that we analyzed in Section 2.3.1 , and these 8 initializers are taken from the spring.factories file. If you don’t remember it, go back and look at it Ha~ Then go down and see the place where the red box is drawn in the picture below. What is the purpose of the code in the red box? In the previous section B.2 , we drew a context relationship inheritance diagram. Do you remember? And this method is to judge whether the context is a generic instance of the class implemented by the traversed initializer , and if so, call the instantiation method of each implementation class. When we finished this method, we found that there were three more elements: why were these three classes put in the application listener, and why were the other five not put in? Let's take a look at the methods implemented by these three classes : the other 5 methods do not call methods , so they will not be placed in the collection of application monitoring classes.
insert image description here

insert image description here
GenericTypeResolver.resolveTypeArgument(initializer.getClass(),ApplicationContextInitializer.class)ApplicationContextInitializerinitializecontext.applicationListeners
RSocketPortInfoApplicationContextInitializer$Listener
ServerPortInfoApplicationContextInitializer
ConditionEvaluationReportLoggingListener$ConditionEvaluationReportListener
insert image description here
initialize
insert image description here
applicationContext.addApplicationListener;

B.2.3 Publishing context prepared events

listeners.contextPrepared(context);
Now when I see what method is called by the listeners, the instinctive reaction is to publish an event: what happens to
insert image description here
the published event after it is received by which listener? ApplicationContextInitializedEventUnfortunately, I didn't find a listener to receive this event. Maybe there are other ways to handle this event? If you have a friend who knows, don’t be reluctant to teach me~

B.2.4 Publishing a Bootstrap Shutdown Event

bootstrapContext.close(context);
The Chinese comment corresponding to this method:

This method is called when BootstrapContextthe is closed and ApplicationContextready.

insert image description here
Obviously, another event is released here BootstrapContextClosedEvent, which is the same as the event prepared by publishing the context in the previous section, but there is still no corresponding listener to handle this event.

B.2.5 Printing the startup log

insert image description here
After these two lines are executed, the two lines of logs in the red box below are printed, indicating that the application is being started, the application name, the jdk version used, the process id, and the project directory. The specific implementation is implemented in the following method, which is relatively simple
insert image description here
. It is the appending of strings and the acquisition of attributes, so I will chatter here.
insert image description here

B.2.6 Bean factory settings

This title sounds a bit like an ice factory. I divided the Bean factory settings into three parts:

1) Application parameter singleton beans and Spring flag singleton beans are added to the cache
2) Whether circular dependencies are allowed and whether the bean definition is allowed to be overwritten
3) Add the Bean factory processor collection to the context
insert image description here

1) Application parameter singleton bean and Spring flag singleton bean are added to the cache.
We click on registerSingletonthe method and enter DefaultListableBeanFactory.registerSingletonthe method. The method called by this method super.registerSingleton(beanName, singletonObject);is DefaultSingletonBeanRegistry.registerSingleton(beanName, singletonObject)the method.
Let's name the picture below as Add Singleton Bean Code Picture , and we will use this picture next.
insert image description here
Did you find any problems when you got here? Obviously we only added two beans, why are there four? Where did the other two come from? At this time, we need to take a time shuttle. Let us go back in time to B.2.2 The initializer is applied to the context to analyze applyInitializers(context);the method, that is, 8 initializers are traversed and initialized, and beanFactorythere are 2 of them after the initializer works. result.
Taking advantage of the heat in the picture above, I want to talk about my heart. The other three of the four caches here registeredSingletonsare Spring’s third-level caches. Let’s not discuss the purpose of this design here. Let’s take a look at these four caches first. The definition of a set:
insert image description here
there must be a reason for this design, don't worry, keep reading. But I want to insert an advertisement first, about the relationship diagram
of two classes: DefaultSingletonBeanRegistryand : because I was wondering why I clicked and entered . I saw that the inheritance relationship is really long~DefaultListableBeanFactory
insert image description here
DefaultSingletonBeanRegistrysuper.registerSingleton(beanName, singletonObject);DefaultListableBeanFactoryregisterSingleton

Advertisement over, back to business, our time machine back to B.2.2 Initializers applied to the context of two initializers: ContextIdApplicationContextInitializerand ConditionEvaluationReportLoggingListener.

  • ContextIdApplicationContextInitializer
    This initializer is to set the ID of the application context. It is explained in the comments of this type. Debug directly into the initialization method. Have you seen a familiar method? After entering it, we added the singleton bean code diagram in registerSingletonfront of it. The process is over, add it to the 2 caches, no more verbosity~~
    insert image description here
  • ConditionEvaluationReportLoggingListener
    It’s an old friend again. We have analyzed it in the three listeners in the application context. The comment says that the purpose of this initializer is to write reports to the log, and it is a bean that does not share instances, that is, a singleton bean .
    Let’s talk about this value again: autoConfigurationReporthow did it come from? With the for loop, we came to ConditionEvaluationReportLoggingListenerthe class, and there is a get method in the if block:
    insert image description here
    After entering from the get method, we lifted autoConfigurationReportthe veil.
    insert image description here
    When you come here, you will know what to do next. That's right, it is the same logic as the previous one for adding a singleton bean code diagram , no more verbosity.
    2) Whether to allow circular dependencies and whether to allow Bean definitions to be overwritten
    insert image description here
    I feel that there is nothing to say here, both if blocks can be entered, and whether to allow circular dependencies and whether to allow Bean definitions to be overwritten are false by default.

3) Add the Bean factory processor collection to the context and
insert image description here
addBeanFactoryPostProcessor add line comments on the method:

Adds a new BeanFactoryPostProcessor that will be applied to this application context's internal bean factory before any bean definitions are evaluated. Called during context configuration.

insert image description here
BeanFactoryPostProcessorIn fact, it is to put the context with it BeanFactoryPostProcessorinto the List collection. The collection definition also says that this is used to update the context, which is the method we will analyze in Section B.3 to update the context . Then we will Save it for later.
Pull the line of sight back, if the value is true, what is added is put into the collection lazyInitializationwithout context , the default value is false, so do not go inside the if block. When we finished this step, we found that there were three elements in it. When were two of them added? Like the first step, B.2.2 initializer is applied to the context , that is, the method corresponding to the initializer in the method is added:BeanFactoryPostProcessorlazyInitialization
beanFactoryPostProcessors
insert image description here
applyInitializers(context);initialize

  • SharedMetadataReaderFactoryContextInitializer
    insert image description here
  • ConfigurationWarningsApplicationContextInitializer
    insert image description here

At this point, the analysis of the growth of the Bean factory has been completed.

B.2.7 Loading resources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));

First look at getAllSourcesthe method. After entering, there is a line comment on this method:

Returns an immutable set of all sources that will be added to the ApplicationContext when {@link #run(String…)} is called. This method combines any primary sources specified in the constructor with any other sources that have been explicitly set @link #setSources(Set) }.

insert image description here
When we analyzed SpringApplicationthe constructor, we mentioned that this primarySourcesis a set collection with a custom startup class class com.fanhf.demo.SpringBootDemoApplication. Now put the elements of this collection into a new unmodifiable Set collection and then put it ApplicationContextin .

Then let's look at loadthe method, first go through the breakpoint and look at the results inside
insert image description here
. There are multiple if blocks inside. The method of drawing the red line is the construction BeanDefinitionLoader, and the inside
getBeanDefinitionRegistry(context)is to return the register defined by the Bean, as shown in the figure below:
insert image description heredebug go In the first if block, why is the context BeanDefinitionRegistryan instance? Do you still remember our context inheritance diagram in Section B.1 ? Is a subclass, so the result is True; and constructed , in order to execute the method. One more thing to add here is that there is a line of code in line 4 of this constructor: , we will mention this constructor method below! ! Let's give the above picture a name: call it BeanDefinitionLoader constructor diagram. Finally, let's look at the method: from here we enter the load method: there is a judgment in the second if block , which means checking whether the Bean meets the registration conditions , the result is True, so I won’t talk about the details, and the next step is to register. I seem to have seen this somewhere ~~ Don’t worry, let’s talk about it later. After this method is finished, the load method is also over.AnnotationConfigServletWebServerApplicationContextBeanDefinitionRegistry
createBeanDefinitionLoaderBeanDefinitionLoaderloader.load();
insert image description here
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
loader.load();
insert image description here

insert image description here
isEligibleannotatedReaderAnnotatedBeanDefinitionReaderregister

All the way to the doRegisterBeanmethod, we pay attention to the registration method of the last line:
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
after entering registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());it, there is a line BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);, and beanName="springBootDemoApplication", obviously there is no in the Map, existingDefinition=false, it comes to the else block of else , that is, the picture below, let’s give this picture a name, and we’ll use it next, let’s call it the Bean injection picture . Turning on the time machine and looking back, it has to be said that I have returned . We have mentioned that there is a registration method in the constructor of this class in the BeanDefinitionLoader constructor diagram and B.1, the context inheritance diagram of the context of creating the application , and the registration entry of these 5 beans is in the method. I will list a few here: the last parameter of the method parameters at the red line is the constant value of . Take the first bean as an example to enter the method: this method is called at this time , this is , and the method is an abstract method in it, which implementation class should I enter to implement this method? Let's look at the inheritance relationship diagram again: it is obvious that it will enter the parent class of this class : at this time, it has entered
insert image description here
beanDefinitionNamesAnnotatedBeanDefinitionReaderAnnotationConfigUtils.registerAnnotationConfigProcessors
insert image description here
registerPostProcessorbeanNameregisterPostProcessor
insert image description here
registry.registerBeanDefinition(beanName, definition);registryAnnotationConfigServletWebServerApplicationContextregisterBeanDefinitionBeanDefinitionRegistry
insert image description here

insert image description here
GenericApplicationContext
insert image description here
DefaultListableBeanFactoryThe implementation of this method in the class is the code in the Bean injection diagram .

The other 4 are also the same operation, which explains the source beanDefinitionMapof beanDefinitionNamesthe elements of the sum. At this point, B.2.7 The process of loading resources is basically explained clearly. Of course, there are some details that will not be mentioned. If you are interested, you can read it in detail~

B.2.8 Publishing context loading events and handling events

listeners.contextLoaded(context);
See also listeners, after entering, you can directly see the corresponding implementation. Inside this method is a for loop, and then publish the event.
Where does the loop data come from? this.application.getListeners()The listener collection data returned by the method is also ~~ We have analyzed it
in section 2.3.1 - D, setting up listeners
insert image description here
, and finally there are 10 listeners. If you forget it, go back and look at it~ You can see it from the red box in the above picture It turns out that these are the 3 initializers that we have analyzed in B.2.2 when the initializer is applied to the context , plus the 10 listeners obtained in the constructor, a total of 13 are in the context , and an event contextis released together after packaging . ApplicationPreparedEventSo does this event have a listener to receive and process it? After searching, I found that there are quite a few. Take one of them as an LoggingApplicationListenerexample. Here, 3 singleton beans have been added to the bean factory, such as springBootLoggingSystem, springBootLoggerGroupsand springBootLoggingLifecycle. Currently, there are 7 special singleton beans in the bean factory, and there is also a RestartApplicationListenerlistener, which is also our What has been mentioned repeatedly before will not be analyzed. Let's see what is done after receiving the preparation event here?
insert image description here
So far, the context preparation step has been analyzed. Generally speaking, it is not difficult, but you need to go back and look at it patiently. However, the following update context is a tough nut to crack, so be prepared~

B.3. Update context

refreshContext(context);
After this method enters, it first adds another class applicationListenersto it : SpringApplicationShutdownHook, which gracefully closes the hook of the SpringBoot application. Together with B.2.8 of the previous analysis, publishing the context loading event and processing the event , plus this hook, there are currently 14 elements in the application listener collection.
insert image description here
Then enter AbstractApplicationContext.refresh(context);the method. The content in this method is the best way to write comments so far. I wrote the Chinese comments below the English comments:

public void refresh() throws BeansException, IllegalStateException {
    
    
		synchronized (this.startupShutdownMonitor) {
    
    
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
			// Prepare this context for refreshing.
			//1、准备更新上下文
			prepareRefresh();
			//Tell the subclass to refresh the internal bean factory.
			//2、告诉子类更新内部的bean工厂
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			//3、准备在此上下文中使用的Bean工厂
			prepareBeanFactory(beanFactory);
			try {
    
    
				// Allows post-processing of the bean factory in context subclasses.
				//4、允许在上下文子类中对Bean工厂进行后处理。
				postProcessBeanFactory(beanFactory);
				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				//5、调用在上下文中注册为Bean的工厂处理器
				invokeBeanFactoryPostProcessors(beanFactory);
				// Register bean processors that intercept bean creation.
				//6、注册拦截 Bean 创建的 Bean 处理器
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();
				// Initialize message source for this context.
				//7、初始化此上下文的消息源
				initMessageSource();
				// Initialize event multicaster for this context.
				//8、为此上下文初始化事件广播
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				//9、初始化特定上下文子类中的其他特殊Bean
				onRefresh();
				// Check for listener beans and register them.
				//10、检查监听器的Beans并注册它们
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				//11、实例化所有剩余(非懒加载初始化)的单例
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				//12、最后一步:发布相应的事件
				finishRefresh();
			} catch (BeansException ex) {
    
    
				if (logger.isWarnEnabled()) {
    
    
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// Destroy already created singletons to avoid dangling resources.
				// 销毁已创建的单例以避免悬而未决的资源
				destroyBeans();
				// Reset 'active' flag.
				//重置active的标识
				cancelRefresh(ex);
				// Propagate exception to caller.
				//将异常告知调用方
				throw ex;
			} finally {
    
    
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

As for the catch block inside, it is relatively simple, so there is no need to analyze it.

B.3.1. Prepare to update the context

prepareRefresh();

Prepares this context for refresh, sets its start date and active id, and performs any initialization of the property source

The first half of the entry method is to set the start date and the active flag to true and log records, which is nothing to say.

We are also divided into three parts here:

1)initPropertySources();
2)getEnvironment().validateRequiredProperties();
3)this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);

1) initPropertySources();
For this method, the Chinese comment is:

Initializes any placeholder property sources in the context

After entering the method, enter again ConfigurableWebEnvironment.initPropertySources, and come to StandardServletEnvironment.initPropertySourcesthe method:
insert image description here
there is only one static method in this method, but the first parameter of the method is a method getPropertySources(), and what this method returns is MutablePropertySources, does this class look familiar? We are analyzing runthe printBanner method of the method A.6.1 and A.6.4 of Part A of the first half are mentioned, but when analyzing A.6.1, A.6.3 and A.6.4, they are basically talking about propertySourceListadding elements to the attributes of this class, a total of 7, Now we WebApplicationContextUtils.initServletPropertySourcescan also see the entry method:
insert image description here
the line here looks a bit messy. In fact, since servletContextand servletConfigare both null, the two if blocks cannot enter. If they can enter, it is the name pointed to by the blue arrow. The corresponding resource class is replaced with the green underlined class. Unfortunately, the calculation here is wrong, and I didn’t give a chance. As for when the replacement will be made, I think it must be later, and we will talk about it when we meet.

2)getEnvironment().validateRequiredProperties();

Validates that all properties marked as required are resolvable
See ConfigurablePropertyResolver#setRequiredProperties

It means to check whether the necessary attributes are missing. Since requiredPropertiesthe size=0, no attributes are marked as required, so there is no need to check. If the necessary attributes are missing, an exception that lacks the necessary attributes will be thrown directly. If the source code later If there is no setting in it, it can be customized.
insert image description here
3) this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);

here is to save the pre-update ApplicationListeners, we have mentioned in B.3, update context and B.2.8 publish context loading event and process event , if you forget You can go back and look at it~
insert image description here
This part actually does 2 things, which are done before, that is, to prepare all the required attribute resources and all the listeners.

B.3.2. Tell the subclass to update the internal bean factory

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
There are relatively few operations here, GenericApplicationContext.refreshBeanFactoryjust an update Bean factory:
insert image description here
the if block of the first entry method is a CAS algorithm, refreshedthe value of the judgment is false, if yes, it is updated to true, in fact, I did not understand the meaning of such judgment and operation, it is If it is not refreshedchanged to true, it means that the operation of updating the context will start from here. Then beanFactoryset serializationIdit to be the same as the context application. I remember that I have also analyzed this. It is probably applyInitializers(context)done by an initializer in the method, and the method finally returns beanFactory.

B.3.3. Bean factories ready to be used in this context

prepareBeanFactory(beanFactory);
There is a line comment on the method:

Configure the factory's standard context features, such as the context's classloader and postprocessor.

beanFactoryPass the parameters obtained by the previous method prepareBeanFactory(beanFactory);to this method.
The method is roughly divided into 4 parts to add:

beanFactory.ignoreDependencyInterface
beanFactory.registerResolvableDependency
beanFactory.addBeanPostProcessor
beanFactory.registerSingleton

Here is a list of where these methods correspond to where the elements are placed:

method collection object element type Element example
addPropertyEditorRegister propertyEditorRegistrars Set< PropertyEditorRegistrar> PropertyEditorRegistrar
addBeanPostProcessor beanPostProcessors List< BeanPostProcessor> ApplicationContextAwareProcessor,ApplicationListenerDetector
ignoreDependencyInterface ignoredDependencyInterfaces Set<Class<?>> EnvironmentAware.class、ApplicationEventPublisherAware.class、pplicationContextAware.class
registerResolvableDependency resolvableDependencies Map<Class<?>, Object> (BeanFactory.class, beanFactory)、(ApplicationContext.class, AnnotationConfigServletWebServerApplicationContext)
registerSingleton registeredSingletons Set< String> “environment”,“systemProperties”
registerSingleton singletonObjects Map<String, Object> (“environment”,ApplicationServletEnvironment)、(“systemProperties”,Properties)

It is easy to guess what it means according to the literal meaning. Take the last one registeredSingletonsas an example, index=0 to 3 has been analyzed in the setting section of the B.2.6 Bean factory , and index=4 to 6 is to publish the context loading event in B.2.8 and The event processing section has been analyzed, and here is a supplementary singleton object with index=7 to 10. The final result is the 11 elements in the figure below: the others are not
insert image description here
listed one by one. If you are interested, you can debug and take a look inside. element.

B.3.4. Allow post-processing of bean factories in context subclasses

postProcessBeanFactory(beanFactory);
This method is an abstract method implemented by multiple subclasses, so which implementation class should we enter? When we analyze this B.3 and update the context , refreshContext(context);there is a context in the method, and the type of this context is: AnnotationConfigServletWebServerApplicationContext:
insert image description here
Let’s look at the inheritance relationship diagram again :
insert image description here
after refreshContext(context);entering the method, go all the way down, and you will reach ConfigurableApplicationContext.refreshthe method, here The real implementation of the method is in AbstractApplicationContext, and there are two subclasses ( ReactiveWebServerApplicationContextand ServletWebServerApplicationContext) that also rewrite the refresh method, but they all call it, super.refresh();so they still come to AbstractApplicationContextit. The method analyzed in this section postProcessBeanFactory(beanFactory);is rewritten by multiple implementation classes, and the current AbstractApplicationContextinside The object of the object actually SpringApplicationcalls refreshthe method in it AnnotationConfigServletWebServerApplicationContext. If you don’t believe me, let’s look at the debug situation:
insert image description here
the this object is AnnotationConfigServletWebServerApplicationContext, so we enter the implementation of this class, but this class is lazy and calls the method of the parent class: super.postProcessBeanFactory(beanFactory);
insert image description here
in this Through the breakpoint in the method, we can see that the following two if blocks are both false, so we only need to analyze super.postProcessBeanFactory(beanFactory);the method. As can be seen from the inheritance diagram
above , the parent class of this class is , and here is the real implementation of the processing bean factory: the first 2 lines in this method and the previous section B.3.3, prepare in this context The two methods of adding elements in the table in the Bean factory used are the same. The key is to look at the blue shadow breakpointServletWebServerApplicationContext
insert image description here
registerWebApplicationScopes();Method:
insert image description here
The two lines before and after this method are very simple, so I won’t talk about it. Focus on the static method in the second line of the method:WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());

After entering, there is a line comment on the method:

Registers a web-specific scope ("request", "session", "global-session") with the given BeanFactory to be used by the WebApplicationContext

registerWebApplicationScopes(beanFactory, null);This static method has only one line of code, and the method is called
insert image description here
, as shown in the figure below: After breakpoint debugging, it is found that only the one in the red box will be executed, the two if blocks are false, and the one in the red box is registered as the comment says of the web application context. The first red box is the registration scope, one is Request, and the other is Session. The elements registered to resolve the dependency collection are all Servlet-related requests, responses, and Session object factory classes.
So this method mainly deals with the registration of the factory of the Web application.

B.3.5. Call the factory processor registered as a Bean in the context

invokeBeanFactoryPostProcessors(beanFactory);

This method and the following registerBeanPostProcessors(beanFactory);methods are basically things in Spring, so I will pick up important analysis.

After the breakpoint enters this method, it comes to the first line of the method. The second parameter of the method's input parameter is a method, and the method returns a List collection. There are 3 elements in getBeanFactoryPostProcessorsthe collection . These three elements are in B. 2.6 The settings of the Bean factory have been analyzed emphatically. After we enter the method, the current instance is entered into this large if block, which is roughly divided into 5 parts,beanFactoryPostProcessors
insert image description here
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());beanFactoryBeanDefinitionRegistry

1) beanFactoryPostProcessorsTraversing the pair, place BeanDefinitionRegistryPostProcessorthe instance of yes and BeanDefinitionRegistryPostProcessorthe instance of not respectively.
2) First, call the implementation PriorityOrderedof BeanDefinitionRegistryPostProcessor
3) Next, call the implementation Orderedof BeanDefinitionRegistryPostProcessor.
4) Finally, all others are called BeanDefinitionRegistryPostProcessoruntil no other handlers are present.
5) Finally at the end, postProcessBeanFactorythe callbacks .

Let's talk about it separately:
1) beanFactoryPostProcessorsTraverse the pair, and place the BeanDefinitionRegistryPostProcessorinstances that are yes and those that are not.BeanDefinitionRegistryPostProcessor
insert image description here
After the for loop is executed, there is a line of comments:

Do not initialize FactoryBeans here: we need all regular uninitialized beans to have the beanFactory postprocessor applied to them! Separate the implemented PriorityOrdered, Orderedones from the restBeanDefinitionRegistryPostProcessors

This line of comments is for 2, 3, 4, let's look down:
2) First, call the PriorityOrderedimplementationBeanDefinitionRegistryPostProcessor
Let's look at postProcessorNamesthe final result first, and then analyze getBeanNamesForType, this method will be used many times in the future ~
insert image description here
click getBeanNamesForType, we Entering DefaultListableBeanFactory.getBeanNamesForType()the method, because configurationFrozenthe initial value is false, so before the value is changed to true, we directly enter the if block: we enter the
insert image description here
method doGetBeanNamesForType, first create a resultList collection, and then have two for loops , let's talk about the first for loop first, this is for beanDefinitionNamestraversal, and the debug shows that there are 7 elements in it.
insert image description here
Where do these 7 elements come from? We have analyzed the source of the elements in the last Bean injection diagram in B.2.7 Loading resources , so I won’t go into details here. Then we enter the method first, and the set of elements entered in the last line of the method is also analyzed in B.2.7 loading resources as we just said . The difference between and in is that the beanName and the corresponding class are placed, and only the beanName is placed. Then I will focus on putting elements in the middle, and the two Boolean values ​​that hinder the placement of elements: and , first look at :beanDefinitionMapbeanDefinitionNames
getMergedLocalBeanDefinitionreturn getMergedBeanDefinition(beanName, getBeanDefinition(beanName));getBeanDefinition(beanName)beanDefinitionMapbeanDefinitionMapbeanDefinitionNamesbeanDefinitionMapbeanDefinitionNames
insert image description here
resultisFactoryBeanmatchFoundisFactoryBean
insert image description here
It can be seen from the above figure FactoryBean.class.isAssignableFrom(beanType)that it is to judge ConfigurationClassPostProcessorwhether to inherit FactoryBean, and the method here isAssignableFromis a native method, and the annotation on this method explains the purpose of this method.
So the value here is false, so we isFactoryBeancan enter the if block: then we have to judge the value. The corresponding method is that there are a lot of codes in this method. I haven’t read all of them, but I will come to the end of this method in the end. One line: After entering this method, there are still many method bodies. I directly hit the breakpoint at the return, so that I can directly know where to jump out, and finally came to: In this if block, the if judgment is a three-eye operation , get arrived, I will write like this in the future. . . We enter the method: here to judge whether it is inherited , we look at the definition of the class in the figure below, it is obviously true, so the value of is also true , so finally the element is put into the collection . And the other 6 are not so lucky, they all use the else block of this method, so they are all false. Let’s talk about the second for loop. The collection of the loop is that there are 11 elements in it. These 11 elements are exactly the same as the elements we mentioned in B.3.3 , the Bean factory that is going to be used in this context . In fact, it is in What is added here is that we did not analyze it at the time. Then iterate over:
insert image description here
matchFoundisTypeMatch(beanName, type, allowFactoryBeanInit);
return typeToMatch.isAssignableFrom(predictedType);
insert image description here

ClassUtils.isAssignable(ourResolved, otherResolved)
insert image description here
ConfigurationClassPostProcessorBeanDefinitionRegistryPostProcessorConfigurationClassPostProcessormatchFound
insert image description here
resultorg.springframework.context.annotation.internalConfigurationAnnotationProcessor
insert image description here
matchFound
insert image description here
manualSingletonNamesregisteredSingletons
insert image description here
manualSingletonNames
insert image description here
This is also isTypeMatch(beanName, type)the judgment made by the use. The 11 elements are all internalConfigurationAnnotationProcessorpassed by the first for loop except beanName=, so there is what we said at the beginning of this section: postProcessorNamesthere is only one element in it internalConfigurationAnnotationProcessor.

Then postProcessorNamesit traversed again, because there is only one element in this collection, it is much faster:
insert image description here
in the if block, currentRegistryProcessorsput internalConfigurationAnnotationProcessorthe corresponding class of beanName=, and
processedBeansput the beanName of this string. currentRegistryProcessors.addThe input parameter of the method is to get the corresponding Bean according to the beanName. There is a method. If beanFactory.getBeanthe implementation class corresponding to this method cannot get the method that the Bean will call, I will not continue to talk about it, otherwise there are really too many.AbstractBeanFactory.doGetBeancreateBean

After the for loop, it is sorted and added ConfigurationClassPostProcessorto registryProcessorsit, and then there is another method that needs to be analyzed. This method will be called repeatedly below, that is, invokeBeanDefinitionRegistryPostProcessorswe enter this method:
insert image description here
as soon as the method comes in, it is a for loop. The key point It is the method at the blue breakpoint. This is an abstract method. Of course, we are entering the ConfigurationClassPostProcessorsub-method of rewriting:
insert image description here
the front of this rewriting method is basically judgment, and we directly enter the last line of the method, which is the inside of the method with the red box :
insert image description here
The above figure is processConfigBeanDefinitionsa method of a do-while loop at 2/3 of the method, and the method I draw a red frame is worth analyzing. These two methods make the elements of the sum at the break point in the above figure surge beanDefinitionMapby beanDefinitionNames7 to 142 ways. The method of the first red box parser.parse(candidates);is to read two properties files: classify the 861 properties of these two files to configure the properties, that is , the 47 elements in the collection in the
insert image description here
previous figure , each of which configClassSome of the elements have multiple elements. Because the parse method has too many nests, I won’t go into details here. If you are interested, you can break in and check, but you need to be patient, otherwise you will be confused. Circled. Here I will simply talk about this.reader.loadBeanDefinitions(configClasses);the processing process of the method. After entering this method, the second line is a for loop:
insert image description here
in the method pointed by the arrow in the above figure, there are two methods of drawing red boxes. After entering the first one, you can find this line of code: the
this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
red in the second for loop After entering the box method, you can find this method:
this.registry.registerBeanDefinition(beanName, beanDefToRegister);
the two methods corresponding to these two methods this.registryare DefaultListableBeanFactorythe methods in the class: the above picture is the place where the elements are
registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
insert image description here
actually added to beanDefinitionMapand . So far the second stepbeanDefinitionNames
2) First of all PriorityOrdered,BeanDefinitionRegistryPostProcessor the analysis of the implementation of the call is almost completed.
3) Next, call the implementation Orderedof BeanDefinitionRegistryPostProcessor.
We found that this place is almost exactly the same as the code in the second step, but we can see from the comments that the second step is to find the implementedPriorityOrderedprocessor, and this step is to find the implementedOrderedpost-processor.
insert image description here
ObviouslypostProcessorNamesthe value is still the same, the corresponding class is stillConfigurationClassPostProcessor, this class is not implemented,Ordered
insert image description here
so the result of the following sorting and other operations is equal to the result of the second step.
But pay attention toinvokeBeanDefinitionRegistryPostProcessorsthe first input parameter of the method:currentRegistryProcessors, which is cleared at the end of the first step, so the for loop will exit directly after the method enters.
4) Finally, all others are called BeanDefinitionRegistryPostProcessoruntil no other handlers are present.
This while loop is actually equivalent to doing nothing. The if judgment drawn in the red box in the figure below is obviously false, and operations such as sorting are cleared in the first stepcurrentRegistryProcessors, and no new elements are put in in the second step. And it was also cleared after the second step, so it cannot continue to execute.
insert image description here5) Finally at the end, postProcessBeanFactorythe callbacks .
According to the debug results in the above figure,registryProcessorsthere are 3 elements,regularPostProcessorsand one element is used asinvokeBeanFactoryPostProcessorsthe first parameter of the method. ForregistryProcessorsthe first two elements in the loop, becauseBeanFactoryPostProcessor.postProcessBeanFactorythe method has not been implemented, only we have analyzed it before. TheConfigurationClassPostProcessoronly way to implementpostProcessBeanFactory:
insert image description here
After entering the method of drawing the red line in the above figure, we directly entered enhanceConfigurationClasses(beanFactory);the method:
insert image description here
in the above figure (because we wanted to put the code in a picture, we folded the if block in the first for loop) is the first for After the loop, in the end, only our custom startup class com.fanhf.demo.SpringBootDemoApplicationpassed the if judgment pointed by the yellow arrow, and was put into the green underlined configBeanDefsMap collection, and our custom startup class was strengthened during the second for loop. Then assign the value to beanClass, which is the breakpoint of the blue shadow.

The regularPostProcessorsprocessor corresponding to an element in the collection is a SpringApplicatioonstatic inner class PropertySourceOrderingBeanFactoryPostProcessorin the implementation postProcessBeanFactorymethod:
insert image description here
we seem to have seen this static inner class somewhere, and add the Bean factory processor collection in part 3 of the B.2.6 Bean factory settings section It has been analyzed in the contextpostProcessBeanFactory , then this method:
and this moveToEndmethod seems to have been seen somewhere, yes, it is exactly the same method in A.6.5 Move the default environment variable to the end .


After talking about invokeBeanFactoryPostProcessorsthe first half of the method, it is the second half. The organization is still very clear: pick out the corresponding beanName corresponding
to the implemented bean from 142 beanNames to traverse: there are 6 elements to traverse, the first Because the first element is directly skipped, the others are put into the immobile List collection in turn according to the conditions met. After putting it into the collection, since the bean corresponding to the beanName is put into the contained collection during the for loop in the figure above , and the sum is completed in the for loop in the figure below, I don’t understand why it doesn’t match The same processing, why bother to convert to the corresponding bean when the cycle is performed again. After converting to the corresponding bean, each method is called , and each post-processor processes it separately. In the end, only the processor inside can do some work. As for what it does, I won’t talk about it first, and I will analyze it later when I use it. The processors of the other two collections are useless and can't do anything.BeanFactoryPostProcessor
insert image description here
processedBeans
priorityOrderedPostProcessorsBeanFactoryPostProcessororderedPostProcessorNamesnonOrderedPostProcessorNamespriorityOrderedPostProcessors
insert image description here
invokeBeanFactoryPostProcessorspriorityOrderedPostProcessors

Let's go back to the initial method of this section invokeBeanFactoryPostProcessors. The above is the analysis of the first static method of this method, followed by an if block. There are 3 conditions in this if condition for "and" judgment, the first two They are all true, but the last one is to judge beanDefinitionMapwhether there is one in it loadTimeVeaver. Unfortunately, it is not, so the operation in if cannot be carried out.
insert image description here
So far, this invokeBeanFactoryPostProcessors(beanFactory);method has finally been roughly analyzed. Since it is basically the contents of the spring component (beans, context, core) package, I have never seen it before, so this method is really painful when I first look at it, because I am walking. Don't know where you are? Fortunately, I calmed down later, and after a little bit of reading, it gradually became clear.

B.3.6. Registering the Bean Processor Created by the Interception Bean

registerBeanPostProcessors(beanFactory);
After entering this method, it is a static method, that is, the red box in the figure below, and the horizontal line is what we have spent a lot of time analyzing in B.3.5, calling the factory processor registered as a Bean in the context. Does it look a little familiar?
insert image description here
With the foundation of B.3.5 , the analysis of this method will become much simpler. The implementation of this method is basically the same as the static method in the previous section, that is, the lower half of the method drawn with a horizontal line in the figure above. Select the corresponding beanName corresponding to the implemented bean from the 143 beanNames for traversal, and finally find 6, then put these 6 into the corresponding collection according to different conditions, and then convert the beanName to the corresponding bean
insert image description here
class BeanPostProcessorPut it into the corresponding processor set:
insert image description here
when traversing orderedPostProcessorNamesand nonOrderedPostProcessorNamestraversing, if the corresponding Bean processor is MergedBeanDefinitionPostProcessoran instance, it will also be placed internalPostProcessorsin it, including priorityOrderedPostProcessors, these four processor sets will be registerBeanPostProcessorsbaptized with methods, Except nonOrderedPostProcessorsthe other three will be sorted again.
So registerBeanPostProcessorswhat is done in the method?
insert image description here
In fact, it is to put all 6 processors in these 4 sets beanPostProcessorsinto it. Let's first look at what elements are in it before
executing the first method: there are already 5 in it, the first 4 are in B.3.3, and we are going to be in As mentioned in the table of the Bean factory used in this context , the last one is put in where we draw the red line in the picture below. After we execute the last method, how many elements are there?registerBeanPostProcessors
insert image description here

insert image description here
registerBeanPostProcessors
insert image description here
There are 11 elements, and after the code in the red box in the above picture is executed, there will be 12 elements.
At this point, registerBeanPostProcessors(beanFactory);the analysis is over, and finally back to refreshthis part of the method:
insert image description here
the two methods of drawing the green frame are what we analyzed in B.3.5 and B.3.6 , which is this section.

B.3.7. Initialize the message source of this context

initMessageSource();

Initialize the message source, if not defined in the context, use the parent class.

insert image description here
From our analysis and the situation of the breakpoint, it is indeed not defined, so the source of the proxy is used, and then injected into the singleton bean factory.

B.3.8. Initialize event broadcast for this context

initApplicationEventMulticaster();

Initializes the application event broadcaster, used if not defined in the contextSimpleApplicationEventMulticaster

Looking at the comments, it feels the same as the previous method: initializing the source of this context is the same operation.
insert image description here
As the comment says, there is indeed no bean factory applicationEventMulticaster, so use it SimpleApplicationEventMulticaster, and then register this in the singleton bean factory SimpleApplicationEventMulticaster.

B.3.9. Initialize other special beans in specific context subclasses

onRefresh();
Analysis in this section : I only select part of the source code for analysis, mainly based on the logs printed on the console. Other interested parties can analyze breakpoints by themselves~
We all know that Springboot comes with Tomcat, and the default port of Tomcat is 8080, so where is this 8080 set? I will reveal the answer in this section.
AfteronRefresh();entering, we then entercreateWebServer();:
insert image description here
the fifth line method in this method:getWebServerFactory();after entering, the method is also usedgetBeanNamesForType, and the bean that inherits the class is found from 143 beansServletWebServerFactory, and there is only onetomcatServletWebServerFactoryclassTomcatServletWebServerFactory, so we enter This class is alsogetWebServerthe method of drawing a red frame. This is an abstract method. We enter the rewritten methodTomcatServletWebServerFactory:
insert image description here
getWebServerthe fourth line of the method createsthe attribute value and attributeTomcatdefined in the Tomcat class.hostnameport;
in this method, there are other methods worth looking at:prepareContext(tomcat.getHost(), initializers);, there are many things in this method, which are some operations before the initialization of Tomcat, I will not write here, otherwise there are too many, interested can go in and have a look.
Let’s go back to port 8080. What we finally use is not defined in the Tomcat class, butcustomizeConnectorin the method drawn in the red box in the above figure:in
insert image description here
this methoddefined in the classin the figure belowThen we come tothe last line of the method:enter this method, and then come tothe constructor:Math.max(getPort(), 0);getPort()AbstractConfigurableWebServerFactory
insert image description here
getWebServerreturn getTomcatWebServer(tomcat);TomcatWebServer
insert image description here
There is one in the constructor. After initialize();entering a line of log in this method will be printed to the console, and the port in the log in the console is put in there as we analyzed earlier.
insert image description here
After executing initializethe first line of code of the method in the above figure, the console will print out the following log. Students who see this may think, we can configure server.port in the application.properties file or xml file, so how do we get this? Just leave a homework here, analyze and check it yourself.
insert image description here
The whole initializemethod body is a synchronization method, adding a built-in synchronizer, and there is a line of code in the synchronizer: , this.tomcat.start();the start();method has two implementation classes, we enter LifecycleBasethe method of the class, we enter this synchronization method, how many in this method The branch and the state are constantly changing. When I first looked at this, I was confused. Fortunately, I walked a few more times later and gradually understood. I was looking at the enumeration value by chance and found that there was a state change on the class Lifecycle. Note, maybe after reading it, you can be less confused.
insert image description here
There is a text description below this state change, take it yourself to check it~
There is a method in the try-catch block startInternal, this method is rewritten by many classes, and it keeps going down, and finally comes to the method StandardServicein the class startInternal:
insert image description here
we see the broken After clicking the blue shaded part in the above picture, a line of log is printed:
insert image description here
so where does this line of log Starting service [Tomcat]come from? The answer lies in the key in the sm.getString method:
insert image description here
followed by the method StandardEngineof the class startInternal:
insert image description here
key is standardEngine.start, and the corresponding value is as shown in the figure below: Starting Servlet engine: [{0}], the value in {0} is replaced with the data in the red box in the picture above
insert image description here
The log printed to the console is as follows:
insert image description here
Where does the version number of Tomcat come from? We startInternal()saw it in the log.info in the method ServerInfo.getServerInfo()), so the version number must be obtained by this line of code ServerInfo. There is a static block:
insert image description here
there is a file with a hard-coded path in the block: /org/apache/catalina/util/ServerInfo.propertieswhen we enter the file, we see the information in the figure below:
insert image description here
it just corresponds to the Tomcat version number in the log; Apache Tomcat/9.0.68
after walking here, I am really stunned I don’t know how many times I cycled between which states. Finally, my patience was exhausted, and I came directly to the place where the log in the picture below is printed: the last line in the picture below is the place where the log above is printed: don’t
insert image description here
be
insert image description here
confused The printing time of my log, yes, I analyzed it yesterday morning, and continued to analyze it after work today, so the time of the log is much different. There are still some places that I don't understand. Let's take a look at it later when I have time. In the above method, another line of log will be printed after the last line:
insert image description here
After some process, the state changes in different states as a cycle, and this cycle has come several times, and has gone through some processes, this onRefresh The method is the end. I feel that I will walk step by step. I will probably have to walk for 24 hours to complete the walk. I will blow it up and analyze it later~~

B.3.10. Check the Beans of the listener and register them

registerListeners();
relax! ! ! ! This method is much simpler!
Divided into three parts:
insert image description here
1) Put the listener back in its new home. Traverse the 14 listeners
we mentioned in B.3, Update Context section and put them in another collection
insert image description here
. 2) Re-find the name of the listener bean Home
This for loop has a comment on it:

Don't initialize FactoryBeans here: we need to leave all regular beans uninitialized for post-processors to apply to them!

This calls the method we analyzed in B.3.5, calling the factory processor registered as a bean in the context and B.3.6, registering the bean processor created by intercepting the bean , and picking out the listener getBeanNamesForTypethat implements the class from all beanNames ApplicationListenerbean name, a total of 6 and put them in the new collection.
insert image description here
3) I want to release early application events, but I think too much.
insert image description here
From the point of view of the breakpoint, because earlyApplicationEventssize=0, the if block cannot enter, and it is over~

B.3.11. Instantiate all remaining (non-lazy-loaded initialization) singletons

finishBeanFactoryInitialization(beanFactory);

Completes the initialization of the bean factory for this context, initializing all remaining singleton beans

To be honest, after a few times of debugging, I found that there configurationFrozenseemed to be a lot of code except for changing (whether the Bean definition metadata can be cached for all Beans) from false to true, but in the end it did nothing because it was filtered.
1) Initialize the conversion service for this context.
insert image description here
Because the beanFactory does not contain it conversionService, it cannot be set.
2) Register the default embedded value parser, which is mainly used for parsing in attribute values.
insert image description here
Because embeddedValueResolversthere is already a parser in it, so there is no need to go down.
insert image description here
In fact, the elements in this embeddedValueResolverscollection should be analyzed in B.3.5, calling the factory processor section registered as a Bean in the context , but because it exceeds the scope of Springboot, it is skipped, and sure enough, it will come out sooner or later. invokeBeanFactoryPostProcessors(beanFactory);I have to pay it back. The method is called
in PostProcessorRegistrationDelegatethe class . This method is an abstract method. Find the rewriting method in the implementation class , and then call the method in this method . This method is also an abstract method. We enter the method of the class , and the last line in this method enters The last line of this method is to add elements to it . There are a lot of calling processes, so I won’t take screenshots one by one. Just look at the source code, I believe you can find it if you are smart~ 3) Initializing LoadTimeWeaverAware is also a bit dramatic. The first method is also in ourinvokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory)postProcessor.postProcessBeanFactory(beanFactory);
PropertyResourceConfigurerpostProcessBeanFactoryprocessProperties(beanFactory, mergedProps);PropertyPlaceholderConfigurerprocessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)doProcessProperties(beanFactoryToProcess, valueResolver);beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);embeddedValueResolvers


insert image description here
B.3.5. Invoking the factory processor section registered as a Bean in the invokeBeanFactoryPostProcessors(beanFactory);context is analyzed in the section, and the matching beanName beanDefinitionNamesis selected from the 143 beanDefinitions in the collection . The result is embarrassing, and there is none. 4) Allows caching of all Bean definition metadata without expecting further changes. Here is a practical thing I said at the beginning in this method. The changed value is true, and this value is called in the method in our figure above. . 5) Instantiate all remaining (non-lazy-initialized) singletons. This method is considered to have the most method body among the five places, mainly because it traverses the 143 beanDefinitions in the collection twice, one time creates and initializes all non-inert singleton beans in the 143 beans, and the other time is A post-initialization callback is fired for all applicable beans. The first for loop is finally the called method, and this method should also be analyzed in section B.3.5, calling the factory processor registered as a Bean in the context . In the second for loop, only the beanName is the corresponding Bean: , which is the subclass that is eligible to enter the if block, and the debug is indeed entered: and then enters the method, but because the condition is not satisfied, nothing happens. did not do.LoadTimeWeaverAware

finishBeanFactoryInitialization(beanFactory);configurationFrozenbeanFactory.getBeanNamesForType

beanFactory.preInstantiateSingletons();
beanDefinitionNames
getBean(beanName);
org.springframework.context.event.internalEventListenerProcessorEventListenerMethodProcessorSmartInitializingSingleton
insert image description here

insert image description here
smartSingleton.afterSingletonsInstantiated();

B.3.12, the last step: release the corresponding event

finishRefresh();

Complete the refresh of this context, call the lifecycle handler's onRefresh() method and publishContextRefreshedEvent

insert image description here

This method is divided into 5 methods:

1) clears resource cache
2) initializes lifecycle handlers for this context
3) broadcasts updates for lifecycle handlers
4) publishes final event
5) participates in LiveBeansView MBean (if active).

What is worth mentioning here is only 2, 3, 4, 1 will not be mentioned, it is to clear the collection, and the if judgment of 5 NativeDetector.inNativeImage()is in B.3.3, and the Bean factory that is going to be used in this context also has this judgment. At that time, there was no Analyze and check the effect yourself.
2) Initialize the life cycle processor of this context
initLifecycleProcessor();
insert image description here
. According to the debug, it is found that the bean factory lifecycleProcessorexists and is defined. If there is no definition, it is like the hint in the comment to go to the else branch. However, no matter whether there is a definition, the final life cycle processor Both are DefaultLifecycleProcessor.

3) Broadcast update for the life cycle processor
getLifecycleProcessor().onRefresh();
This method should be the most complicated method among the 5 methods.getLifecycleProcessor()What we return is the initialization we mentioned aboveDefaultLifecycleProcessor, and call the method in this classonRefresh():after
insert image description here
this method is executedstartBeansIt is to set the running state to true, we enterstartBeanthe method, first callgetLifecycleBeans();the method:
insert image description here
after this method enters, it also calls what we mentioned in the previous section in the third linebeanFactory.getBeanNamesForType();, this time the main type isLifecycle, and finally only found There are 4 inheritedLifecyclebeanNames, and there is an if judgment in the code above tolifecycleProcessorfilter out the beanName, so there are 3 left, namely the three key-value elements shown in the beans in the figure below .
insert image description here
After this method is over, we will come backstartBeans. There are not many codes here, but I did spend some time understanding one of the methods in it,phases.computeIfAbsent()that is, the way of writing . The blog written by the guy , after running the examples, I realized that this method does save a lot of code. If you are not familiar with it, you can read and run the examples to understand it well. Here is a homework to come again. Let's play~
insert image description here
After the final forEach method is over,phasesput 3 key-value pairs in the Map corresponding to the variable, that is, thephasesobject at the break point in the above figure. The type of this object isMap<Integer, LifecycleGroup>, andLifecycleGroupthe value inside isDefaultLifecycleProcessor
an internal class of , as shown in the figure below, so the method at the breakpoint of the blue shadeaddisLifecycleGroupthe method of the class.
insert image description here
whenforEachAfter the method ends, the value of the Map of phases is traversed, the method is executed LifecycleGroup, startand startthe method calls the method in the for loop doStart. The first thing to traverse is that LoggingApplicationListeneras soon as the method comes up, the spirngBootLoggingLifecyclecache of the beanName is deleted, and then the method is called in the try-catch LoggingApplicationListener, startand this type of method and the corresponding bean startto be looped for the third time : the method, All just change the state of the from false to true. When the cycle arrives , it is different, and enters the method of the class : the method in the above figure is to call the method, and the value of is that the constructor of this class is called in the method, that is, we are in B.3.9, initialization Other special beans in specific context subclasses should be analyzed, but not elaborated. Then we come to the method: after executing the blue shaded breakpoint in the above figure, the console prints the log in the figure below. 4) Publish the final event Publish the event, then only the following 4 classes will receive and process this event. For example , and , roughly clear the corresponding cache. So far B.3, update the contextwebServerGracefulShutdownWebServerGracefulShutdownLifecyclestartrunning
insert image description here
webServerStartStopWebServerStartStopLifecyclestart
insert image description here
startthis.webServer.start();webServerTomcatWebServerWebServerStartStopLifecyclecreateWebServerTomcatWebServerstart
insert image description here

insert image description here

publishEvent(new ContextRefreshedEvent(this));
ContextRefreshedEvent
insert image description here
ClearCachesApplicationListenerSharedMetadataReaderFactoryBean
insert image description here
insert image description here
The basic analysis is completed. Of course, there are some things that are not mentioned in this article. You can check the breakpoints by yourself~

B.4. Post-processing of update context

It's very simple here, because this method is currently empty, and users can implement it themselves.

B.5. How long did it take for the printing application to start up?

After executing this line of code, you can see a sentence printed on the console:
insert image description here
After reading getStartedMessagethe appended string in the method, it is clear.
insert image description here

B.6. Publish the event of context start

listeners.started(context, timeTakenToStartup);
We seem to see old friends again. Entering the started method, we can see that SpringApplicationRunListenersa large number of methods in this class are called in the class
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction).
insert image description here
Enter listener.staredthe method and see that there is a line comment that means:

The context has been refreshed and the application has started, but CommandLineRunnerthe ApplicationRunnerapplication runner has not yet been called.

Enter the called SpringApplicationRunListeners.startedmethod:
insert image description here
It is found that this method has no method body and is marked as @Deprecateda defaultmethod. Something feels wrong, why didn't it come true? At that time, I didn't understand it. After repeated debugging, I set a breakpoint in the publish method, and after chasing the caller from publish, I realized that the real implementation should be EventPublishingRunListenerimplemented in it, because SpringApplicationRunListenersthere is only one implementation class. @DeprecatedWhy didn't I go directly to this method of the implementation class from the unmarked startedmethod? I should drill down instinctively. Careless.
insert image description here
In the method EventPublishingRunListenerinside started, you can see that the event is published.
insert image description here
After the event is released, it finally enters the method AbstractApplicationContextinside publishEventto release the time.
insert image description here
Entering this method, multicastEventyou can see the method we have analyzed before:
insert image description here
here is to broadcast this event to the 4 listeners we analyzed before, and the 4 listeners will judge whether they are capable of handling this event.

So ApplicationStartedEventwhich listener handles the event? I tried to search globally but couldn't find a listener for this event. So here I don't quite understand what is the use of events that are not processed by listeners after they are published? Leave a homework!

In addition, add a sentence: the method here and the method in section B.9started below are all through calling the method, then calling the method, and then broadcasting the event, and we are directly calling the method in the method analyzed in section A.5 . I actually I don't quite understand why and the method have to go a long way. There must be a reason for existence, and if you look at it later, you may understand.readycontext.publishEventpublishEventmulticastEventlistener.startingmulticastEventstartedready

B.7. Invoking the Runner

callRunners(context, applicationArguments);
insert image description here
ApplicationRunnerSince there is no and in the context CommandLineRunner, the runners collection is empty, and the method ends directly when it reaches for. ApplicationRunnerSo how can we get the sum from the context CommandLineRunner? The answer is to customize the implementation class to implement the run method of one or two of these two interfaces.

B.8. Handle caught running exceptions

The run method in the figure below is the second part of our analysis. There are two try-catch in it, and there is a method to handle running exceptions in the catch block. Let's analyze whether there is something in this method.
insert image description here
Enter handleRunFailurethe method:
insert image description here
only these 4 places can be analyzed, and the last line directly throws an exception. I won't analyze it.

B.8.1, Coding for Handling Exit

handleExitCodeAn event was published ExitCodeEvent, but I still can't find a listener for this event.

B.8.2. Publish failed events to listeners

Entering the failed method, we can see that this method calls the doWithListenersmethod again. This method calls the method of drawing the red line callFailedListener. There is a method in the try block in this method listener.failed(context, exception);. Are you familiar with it?
insert image description here
The implementation of this failed method is still It is in EventPublishingRunListenerthe class.
insert image description here
In the if-else here, regardless of whether the context is empty or active, it is guaranteed that the failed event can be broadcasted. But obviously, if the if branch is taken, the active attribute here is set to true prepareRefreshwhen the update context we analyzed earlier is executed this.active.set(true);. If it is false, it means that there is a bug before the update context is reached. Take the else branch. So which listener is this ApplicationFailedEventevent received and processed? I searched this event globally, and I am an old friend again RestartApplicationListener:
insert image description here
this method goes all the way down, and you reach the revome method. This method is to delete the context from the collection, and this collection, I remember I mentioned it before, is all written time. A copied collection, but the function is different. The aforementioned is for attribute resources, and this one is for context:private final List<ConfigurableApplicationContext> rootContexts = new CopyOnWriteArrayList<>();

B.8.3. Making a failure report

reportFailure(getExceptionReporters(context), exception);
According to the name of the method, it can be seen that this error was reported, how was it reported, and what did it do? Let’s look at the method first getExceptionReporters(context):
insert image description here
Well, it’s a familiar formula and a familiar taste. We searched directly globally, and SpringBootExceptionReportersure enough, we found its implementation class under spring-boot-2.7.5.jar FailureAnalyzers:
insert image description here
so getExceptionReportersthe input parameters of the method are FailureAnalyzers, that is, there is only one element in the collection in the figure below exceptionReporters, which is the failure analyzer FailureAnalyzers, then the for loop will only loop once and call reporter.reportException(failure)the method .
insert image description here
From the method of drawing the red line, we have arrived at the site. Let’s look at the methods FailureAnalyzersof this type : from then on, we enter the interface . There is only one method in this interface: , enter the implementation class of this method, and we come to this interface The abstract implementation class of this abstract implementation class found that there are actually many analyzers, and each analyzer performs customized processing of error messages. I won't give an example here. Those who are interested can check it out for themselves. When the class is returned , it is used as the input parameter of the method: when you see the method in the first line of the method, you can search instinctively , and you can find the implementation class in the package: let’s take a look at the implementation of report by the log record error analysis reporter: here It is to print the error information to the log file. In addition, I also found the log when the spring-boot failed to start before. It turns out that I learned from here and unveiled the mystery.reportException
insert image description here
analyzeFailureAnalyzeranalyzeAbstractFailureAnalyzeranalyze
insert image description here
FailureAnalysisreport
insert image description here
loadFactoriesFailureAnalysisReporterspringt-boot-2.7.5.jar/META-INF/spring.factoriesLoggingFailureAnalysisReporter
insert image description here

insert image description here

B.8.4. Delete the registered context

shutdownHook.deregisterFailedApplicationContext(context);
When the program reaches the end of creation and ConfigurableApplicationContextan exception occurs, then this method of unregistering the application context will be executed.
This method is SpringApplicationShutdownHookimplemented in a class, and there is a line of annotations on this class:

A Runnable to use as addShutdownHook to perform a graceful shutdown of a Spring Boot application. This hook keeps track of registered application contexts and any operations registered via SpringApplication#getShutdownHandlers.

Let's look at the implementation of the method:
insert image description here
in fact, the application context object in the set collection is deleted.

B.9. Publish ready events

listeners.ready(context, timeTakenToReady);This method is similar to the started method in Section B.6, enter listener.ready(context, timeTaken))the method, you can see the note:

Called immediately before the run method completes, when the application context has been refreshed, CommandLineRunnersand all sums ApplicationRunnershave been called.

Enter EventPublishingRunListenerthe ready method inside, and the operation of the started method is the same, but the event class is changed.
insert image description here
After publishing the event, the corresponding listener will proceed to the next step after receiving the event. The following figure is the listening class that processes the received events RestartApplicationListener. Seeing the picture below is not very familiar, because I also took a screenshot of this picture in section A.5 , but the analysis was ApplicationStartingEventthe event at that time.
insert image description here
So far, Springboot has been started, and our treasure hunt is over~

3. Homework

According to what I don’t understand during the analysis process, I will leave a homework for myself, and I will read it again later, and then make up:
1) Use ConversionServicethe completed homework to convert the number of String type to Integer: Link
2) application.propertiesFile or xml file How to get the configuration server.portin the program?
3) ApplicationStartedEventWhich listener will handle the event after it is published?
4) computeIfAbsentCorresponding demo, completed homework: link

4. Expansion

In fact, there are some places where you can expand and learn, such as the following, I feel that they can be used in future work
1) Custom banner
2) Event release demo
3) Custom error report
4) Execute custom run method

V. Summary

From reading the source code, to debugging over and over again, to understand from front to back, I found that the startup process of Springboot uses a lot of SPI and event publishing. In these two ways, I usually use event publishing many times to decouple the business. It is a very good way to deal with it. I didn’t know where the idea of ​​​​event handling encapsulated by our big guys came from. Now I probably found the source, so I feel painful and happy in the process of reading the source code. The painful point is Sometimes I don’t know the reason for this implementation. After reading it more than ten times, I still don’t understand how to get to a certain line of code. This article also explains what I don’t understand; the happy point is that I finally know how some logs are. It has appeared, what excellent ideas are used in it, and what points can be expanded, it feels very enlightening.

Of course, there are still places that I don’t understand, and the understanding of some places may need to be corrected, so if you have any questions during the reading process, please leave a message for advice~

This analysis of the SpringBoot source code, from the beginning of the analysis to the completion of the blog today, it took almost a month and a half intermittently. The time is very long, but I think it should open a door to the cultivation of my inner strength. Source code, I can use the method of analyzing source code this time to analyze other source codes. In short, after reading it, I really have a lot of gains, and I will continue to work hard in the future! ! !

Guess you like

Origin blog.csdn.net/fhf2424045058/article/details/127654668