[SpringBoot] SpringBoot startup source code analysis

Overview

1. SpringBoot startup process source code analysis
2. Extension point source code analysis during SpringBoot startup process
3. SpringBootConfiguration File Priority Analysis
4. SpringBoot has built-in Tomcat to start source code analysis

SpringBoot startup class:

@SpringBootApplication
public class MyApplication {<!-- -->
public static void main(String[] args) {<!-- -->
SpringApplication.run(MyApplication.class, args);
}
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {<!-- -->
    //Construct the SpringApplication object first, and then execute the run method
return new SpringApplication(primarySources).run(args);
}

Construct SpringApplication object

image.png

org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class…)

1. Speculate web application types

image.png

org.springframework.boot.WebApplicationType#deduceFromClasspath

illustrate:

  1. If org.springframework.web.reactive.DispatcherHandler exists in the project dependency, and org.springframework.web.servlet.DispatcherServlet does not exist, then the application type is WebApplicationType .REACTIVE
  2. If org.springframework.web.reactive.DispatcherHandler does not exist in the project dependencies, nor does org.springframework.web.servlet.DispatcherServlet exist, then the application type is WebApplicationType.NONE
  3. Otherwise, the application type is WebApplicationType.SERVLET

2. Obtain the BootstrapRegistryInitializer object

  1. Read the extension point whose key is the BootstrapRegistryInitializer type from “META-INF/spring.factories” and instantiate the corresponding extension point object, that is, the SPI mechanism
  2. The function of BootstrapRegistryInitializer is to initialize BootstrapRegistry
  3. The above DefaultBootstrapContext object is a BootstrapRegistry, which can be used to register some objects. These objects can be used in the process from SpringBoot startup to the completion of Spring container initialization.
  4. My understanding: Before the Spring container, BootstrapRegistry was used to share some objects. After the Spring container was created, the Spring container was used to share some objects.

3. Obtain the ApplicationContextInitializer object

  1. Read the extension point whose key is ApplicationContextInitializer from “META-INF/spring.factories” and instantiate the corresponding extension point object
  2. As the name suggests, ApplicationContextInitializer is used to initialize the Spring container ApplicationContext object. For example, you can use ApplicationContextInitializer to add ApplicationListener to the Spring container.

4. Obtain the ApplicationListener object

  1. Read the extension point whose key is the ApplicationListener type from “META-INF/spring.factories” and instantiate the corresponding extension point object
  2. ApplicationListener is a listener in Spring and is not a new concept in SpringBoot

5. Infer the Main class (the class where the main() method is located)

It has no specific effect. The logic is to determine which class the main() method is in based on the call stack of the current thread, and which class is the Main class.

run(String… args) method

  1. Create DefaultBootstrapContext object
  2. Use BootstrapRegistryInitializer to initialize the DefaultBootstrapContext object
  3. Get SpringApplicationRunListeners

6. Trigger starting() of SpringApplicationRunListener

By default, SpringBoot provides an EventPublishingRunListener, which implements the SpringApplicationRunListener interface. By default, EventPublishingRunListener is used to publish an ApplicationContextInitializedEvent event. Programmers can consume this event by defining ApplicationListener.

7. Create Environment object

The Environment object represents environment variables. The object mainly contains:

  1. Environment variables of the current operating system
  2. Some configuration information of JVM
  3. JVM environment variables configured in -D mode

8. Trigger environmentPrepared() of SpringApplicationRunListener

By default, EventPublishingRunListener will be used to publish an ApplicationEnvironmentPreparedEvent event. Programmers can define ApplicationListener to consume this event. For example, by default, there will be an EnvironmentPostProcessorApplicationListener to consume this event. After receiving this event, this ApplicationListener will parse application.properties, application.yml files and add them to the Environment object.

9. Print Banner

Nothing special.

10. Create Spring container object (ApplicationContext)

Will use ApplicationContextFactory.DEFAULT to create the corresponding Spring container according to the application type.
ApplicationContextFactory.DEFAULT is:

ApplicationContextFactory DEFAULT = (webApplicationType) -> {<!-- -->
    try {<!-- -->
        switch (webApplicationType) {<!-- -->
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
        }
    }
    catch (Exception ex) {<!-- -->
        throw new IllegalStateException("Unable create a default ApplicationContext instance, "
                                         + "you may need a custom ApplicationContextFactory", ex);
    }
};

so:

  1. The application type is SERVLET (Tomcat/Jetty), which corresponds to AnnotationConfigServletWebServerApplicationContext
  2. The application type is REACTIVE (Spring WebFlux), which corresponds to AnnotationConfigReactiveWebServerApplicationContext
  3. If the application type is a common type (Spring application), it corresponds to AnnotationConfigApplicationContext

11. Use ApplicationContextInitializer to initialize the Spring container object

By default, SpringBoot provides multiple ApplicationContextInitializers, the more important of which is ConditionEvaluationReportLoggingListener. Don’t see that its name is XXXListener, but it does implement the ApplicationContextInitializer interface.

In its initialize() method it will be:

  1. Assign the Spring container to its applicationContext property
  2. And add a ConditionEvaluationReportListener (internal class of ConditionEvaluationReportLoggingListener) to the Spring container, which is an ApplicationListener
  3. And generate a ConditionEvaluationReport object assigned to its report attribute

The ConditionEvaluationReportListener will be responsible for receiving the ContextRefreshedEvent event, that is, once the Spring container is started, the ContextRefreshedEvent will be triggered, and the ConditionEvaluationReportListener will print the condition evaluation report of the automatic configuration class.

12. Trigger contextPrepared() of SpringApplicationRunListener

By default, EventPublishingRunListener will be used to publish an ApplicationContextInitializedEvent event. By default, no ApplicationListener has consumed this event.

13. Call close() of the DefaultBootstrapContext object

Nothing special, ignore.

14. Register the startup class as a configuration class in the Spring container (load() method)

Pass in the class in SpringApplication.run(MyApplication.class);, such as MyApplication.class, as the configuration class of the Spring container.

15. Trigger contextLoaded() of SpringApplicationRunListener

By default, EventPublishingRunListener is used to publish an ApplicationPreparedEvent event.

16. Refresh Spring container

Calling the refresh() method of the Spring container, combined with steps 9 and 13, is equivalent to executing such a process:

  1. AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  2. applicationContext .register(MyApplication.class)
  3. applicationContext.refresh()

17. Trigger started() of SpringApplicationRunListener

Publish the ApplicationStartedEvent event and AvailabilityChangeEvent event. The AvailabilityChangeEvent event indicates the state change status. The changed status is LivenessState.CORRECT

The LivenessState enumeration has two values:

  1. CORRECT: Indicates that the current application is running normally
  2. BROKEN: Indicates that the current application is still running, but there is an internal problem, and it has not been found where it is used.

18. Call ApplicationRunner and CommandLineRunner

  1. Get the Bean of type ApplicationRunner in Spring container
  2. Get the Bean of type CommandLineRunner in Spring container
  3. Execute their run()

19. Trigger ready() of SpringApplicationRunListener

Publish the ApplicationReadyEvent event and AvailabilityChangeEvent event. The AvailabilityChangeEvent event indicates the status change status. The changed status is ReadinessState.ACCEPTING_TRAFFIC

The ReadinessState enumeration has two values:

  1. ACCEPTING_TRAFFIC: Indicates that the current application is ready to receive requests
  2. REFUSING_TRAFFIC: Indicates that the current application refuses to receive requests. For example, when Tomcat is closed, the AvailabilityChangeEvent event will be released and the status is REFUSING_TRAFFIC.

20. If the above process throws an exception, the failed() of SpringApplicationRunListener will be triggered

Publish the ApplicationFailedEvent event.

Configuration file analysis

Related to custom configuration centers, such as nacos, loading configuration information into Environment objects.

Create Environment object

Add servlet, servlet container, operating system, and JVM environment variables, such as:

# JVM environment variables
java -jar -Dserver.port=8888 springbootdemo.jar
# Command line variables
java -jar springbootdemo.jar --server.port=8888

Priority high to low:

  1. servletConfigInitParams
  2. servletContextInitParams
  3. systemProperties
  4. systemEnvironment

Add DefaultProperties

Add defaults, set through SpringApplication’s setDefaultProperties() method.

Priority high to low:

  1. servletConfigInitParams
  2. servletContextInitParams
  3. systemProperties
  4. systemEnvironment
  5. defaultProperties

Command line parameter PropertySource

Encapsulate the parameters in the command line into PropertySource and add them to the Environment object.
Add the parameters set in the runtime command line, such as:

java -jar springbootdemo.jar --server.port=8888

Priority high to low:

  1. commandLineArgs
  2. servletConfigInitParams
  3. servletContextInitParams
  4. systemProperties
  5. systemEnvironment
  6. defaultProperties

Add a RandomValuePropertySource

Adding one that can generate random numbers is useless.

Priority high to low:

  1. commandLineArgs
  2. servletConfigInitParams
  3. servletContextInitParams
  4. systemProperties
  5. systemEnvironment
  6. random
  7. defaultProperties

Read configuration from Environment

Read the spring.application.json configuration from the existing Environment and add the -Dspring.application.json parameter configuration.

Priority high to low:

  1. commandLineArgs
  2. spring.application.json
  3. servletConfigInitParams
  4. servletContextInitParams
  5. systemProperties
  6. systemEnvironment
  7. random
  8. defaultProperties

Add cloud platform related information

If currently on CLOUD_FOUNDRY, add platform dependent.

Priority high to low:

  1. commandLineArgs
  2. vcap
  3. spring.application.json
  4. servletConfigInitParams
  5. servletContextInitParams
  6. systemProperties
  7. systemEnvironment
  8. random
  9. defaultProperties

Get the specified path configuration

Get the path specified by the following three parameters:
spring.config.location (if specified, the default will be invalid)
spring.config.import (newly added on the basis of default)
spring.config.additional-location (newly added based on the default)

Find the configuration file directory in the following order:
classpath:/firechou/, added through configuration;
optional:file:./config/*/;
optional:file:./config/;
optional:file:./;
optional:classpath:/config/;
optional:classpath:/;
optional means optional, no error will be reported if the file is not available;
file represents the directory where the packaged jar file is located;
classpath represents the directory where target is located;

Go to each directory to find the configuration file. You can configure the file name through spring.config.name. The default is application.
as follows:
classpath:/firechou/application.yaml, application.yml, application.xml, application.properties;
file:./application.yaml, application.yml, application.xml, application.properties;
file:./config/application.yaml, application.yml, application.xml, application.properties;
file:./config/*/application.yaml, application.yml, application.xml, application.properties;
classpath:/application.yaml, application.yml, application.xml, application.properties;
classpath:/config/application.yaml, application.yml, application.xml, application.properties;

illustrate:
In reverse order, so the priority of properties is higher than yml. It actually depends on the specific springboot version;
The configuration file path specified by the user has the highest priority. By default, the file directory is higher than the classpath directory;

Parse all the above paths and parse the real existing file paths.
Note that here we do not parse each file to generate a PropertySource and add it to the Environment object. We just parse out the PropertySource object first and put it into a collection in order.

Find spring.profiles.active from all the above configurations, find the corresponding value, and then find the corresponding file according to the specified value, such as dev.

In each configuration file, you can specify the prerequisites for the configuration file to take effect in the following ways:
spring.config.activate.on-profile=dev;
spring.config.activate.on-cloudPlatform=CLOUD_FOUNDRY;

Filter configuration

Filter according to the currently supported profiles and cloudPlatform. Those that are not configured in the configuration file and those that match will win;
Add the corresponding PropertySource generated by the final winning configuration file to the Environment;

Priority high to low:

  1. commandLineArgs
  2. vcap
  3. spring.application.json
  4. servletConfigInitParams
  5. servletContextInitParams
  6. systemProperties
  7. systemEnvironment
  8. random
  9. application-dev.properties
  10. application-dev.yml
  11. application.properties
  12. application.yml
  13. defaultProperties

SpringBoot complete configuration priority

Refer to the official website: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

Priority from low to high:

1.Default properties (specified by setting SpringApplication.setDefaultProperties). //Default configuration
2.@PropertySource annotations on your @Configuration classes. Please note that such property sources are not added to the Environment until the application context is being refreshed. This is too late to configure certain properties such as logging.* and spring.main.* which are read before refresh begins.
3.Config data (such as application.properties files). // Configuration in the classpath or jar directory.
4.A RandomValuePropertySource that has properties only in random.*.
5.OS environment variables. // Operating system environment variable parameters
6.Java System properties (System.getProperties()). // JVM parameters
7.JNDI attributes from java:comp/env. // Ignore it
8.ServletContext init parameters.
9.ServletConfig init parameters.
10.Properties from SPRING_APPLICATION_JSON (inline JSON embedded in an environment variable or system property).
11.Command line arguments. // command line
12.properties attribute on your tests. Available on @SpringBootTest and the test annotations for testing a particular slice of your application.
13.@TestPropertySource annotations on your tests.
14.Devtools global settings properties in the $HOME/.config/spring-boot directory when devtools is active.

illustrate:

  1. Depending on the springboot version, the priorities of properties and yml files are different. The properties of the new version have a higher priority than yaml;
  2. bootstrap.properties is a specific configuration file in springcloud;