What is the SpringBoot source code

Time:2022-8-5

What is the SpringBoot source code

1 Introduction
Spring Boot is a rapid development framework provided by the Pivotal team. Based on SpringMVC, it simplifies XML configuration through annotations + built-in Http server such as: tomcat-embed-core, and quickly integrates some commonly used third-party dependencies (inheriting dependencies through Maven), The final implementation is executed as a Java application.

1.1 Origin of SpringBoot
Spring Framework: The Spring Framework has derived many products from the early IOC and AOP, such as Spring (boot, security, jpa), etc.
SpringMVC framework: Spring MVC provides a lightly coupled way to develop web applications, it is a web framework of Spring. Developing web applications is easy with Dispatcher Servlet, ModelAndView and View Resolver. The problem area to be solved is website application or service development – URL routing, Session, template engine, static Web resources, etc., is an MVC framework based on Spring.
SpringBoot framework: Spring Boot implements automatic configuration and reduces the complexity of project construction. It is mainly to solve the problem that using the Spring framework requires a lot of configuration to be too troublesome, so it is not a solution to replace Spring, but a tool that is closely integrated with the Spring framework to improve the experience of Spring developers. At the same time, it integrates a large number of commonly used third-party library configurations (such as Jackson, JDBC, Mongo, Redis, Mail, etc.), and is a rapid development integration package based on Spring4's conditional registration.
image description

1.2 Convenient starter poms (starter)
The starter contains the dependencies needed to set up a project to run quickly. It is a collection of dependency descriptors. When the application needs another service of spring, there is no need to paste and copy a large number of dependency descriptors. For example, if you want to use redis in spring, you only need to include the spring-boot-starter-redis dependency in your project. All starters follow a similar naming pattern: spring-boot-starter-, here is a special type of application. This naming structure can help you find the starter you need. Maven integrated with many IDEs allows you to search for dependencies by name.

1.3 demo
@SpringBootApplication
public class DemoApplication {

public static void main(String[] args) {
  SpringApplication.run(DemoApplication.class, args);
}

}
This is a simple SpringBoot startup class implementation, and the next article will expand and analyze this demo.

1.4 SpringBoot startup process
clipboard.png

2 @SpringBootApplication annotation analysis
The following is the source code implementation of the annotation @SpringBootApplication

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {

    @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {
It can be found that it is composed of many annotations. The following is a detailed analysis of the role of each annotation here.

@Target Target specifies the enumeration set of the range that the annotation can use through ElementType (FIELD/METHOD/PARAMETER…)
@Retention The Retention annotation indicates that this type of annotation will be retained until that stage. There are three values:

RetentionPolicy.SOURCE – Annotations of this type are only retained at the source level and are ignored at compile time
RetentionPolicy.CLASS – Annotations of this type are retained at compile time and exist in the class file, but will be ignored by the JVM
RetentionPolicy.RUNTIME – Annotations of this type will be retained by the JVM, so they can be used by the JVM or other
The @Documented annotation indicates that this annotation should be documented by the javadoc tool. By default, javadoc does not include annotations. But if @Documented is specified when declaring an annotation, it will be processed by tools such as javadoc, so the annotation type information will also be is included in the generated documentation
@Inherited allows subclasses to inherit parent class annotations, which are only useful for class annotations, not for methods and properties.
The @SpringBootConfiguration annotation actually has the same function as @Configuration. The class equipped with this annotation can complete some configurations in the way of JavaConfig, and can no longer use XML configuration.
The @ComponentScan annotation completes the automatic scanning function, which is equivalent to the Spring XML configuration file:<context:component-scan> , You can use the basePackages property to specify the packages to be scanned and the conditions for scanning. If it is not set, it will scan the class of the same level and all the classes in the same level directory by default. Therefore, our Spring Boot project will generally put the entry class in the top-level directory, so as to ensure the source code directory. All classes can be scanned.
The @EnableAutoConfiguration annotation is the key annotation that makes Spring Boot configuration so simple. I put the implementation of EnableAutoConfiguration up, let's appreciate it!

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
The @AutoConfigurationPackage annotation is used to save auto-configuration classes for later use, such as for JPA entity scanners to scan entity classes defined by developers by annotating @Entity. In layman's terms, the registered bean is defined in the container.
@Import(AutoConfigurationImportSelector.class) is the most critical part of the EnableAutoConfiguration annotation. With the help of AutoConfigurationImportSelector, it can help SpringBoot applications load all eligible @Configuration configurations into the current IoC container created and used by SpringBoot. There is still more to say about the @Import annotation, let’s talk about it another day.
Let’s talk about the topic of annotations here first, and let’s start the coding link.

3 Dissecting the code
Looking at the source code of SpringApplication, you can find that the startup of SpringApplication consists of two parts:

new SpringApplication(primarySources): Create a SpringApplication object
run(args): call the run method
3.1 Instantiate the SpringApplication object
The source code is as follows:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;//1, initialize the resource loader
    Assert.notNull(primarySources, &quot;PrimarySources must not be null&quot;);//2. Assert that the resource loading class cannot be null
    this.primarySources = new LinkedHashSet&lt;&gt;(Arrays.asList(primarySources));//3, initialize and load the resource class collection and deduplicate
    this.webApplicationType = deduceWebApplicationType();//4. Infer whether the application type is Standard or Web
    setInitializers((Collection) getSpringFactoriesInstances(
            ApplicationContextInitializer.class));//5, set the application context initializer
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//6, set listeners 
    this.mainApplicationClass = deduceMainApplicationClass();//7, infer the application entry class
}

The following will conduct a detailed analysis of the important implementations in the source code.

3.1.1 Initializing the resource loader
The ResourceLoader interface is used to load resources in Spring, through which a Resouce object can be obtained. Friends who use spring know that there are many ways to load resources. Let's start with two commonly used interfaces and implementation classes that inherit ResourceLoader.

DefaultResourceLoader: As a direct implementation class of the ResourceLoader interface, this class implements the basic resource loading function and can realize the loading of a single resource.
ResourcePatternResolver: This interface inherits ResourceLoader and defines the method of loading multiple resources, which can realize the loading of multiple resources.
1、DefaultResourceLoader
As described above, this class implements the function of loading a single resource by implementing the ResourceLoader interface. Its subclasses implement specific resource access policies by inheriting from it. Let's explore how this class loads a single resource:

public Resource getResource(String location) {//Here are three ways to identify the location and load the Resource.
    Assert.notNull(location, "Location must not be null");
    //1. First, see if there is a custom ProtocolResolver. If there is, analyze the location according to the custom ProtocolResolver to get the Resource
    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }
    //2. Parse the ClassPathResource according to whether the path matches &quot;/&quot; or &quot;classpath:&quot;
    if (location.startsWith("/")) {
        return getResourceByPath(location);
    }else if (location.startsWith(CLASSPATH_URL_PREFIX)) {//classpath
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    }else {
        try {
            //The default incoming location is a URL path, and a UrlResource is obtained after loading
            URL url = new URL(location);
            return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
        }
        catch (MalformedURLException ex) {
            // If the above three conditions are not satisfied, it will be processed according to &quot;/&quot;
            return getResourceByPath(location);
        }
    }
}

Literacy: ProtocolResolver is a custom extension class for parsing locations. With it, we can freely pass in locations in different formats, and then parse and obtain our Resource according to the corresponding format.
Extension: During the initialization of the Spring container, we can customize a class to implement the ProtocolResolver interface, and then implement the resolve method to resolve a specific location to get the Resoure.

2、ResourcePatternResolver
This interface inherits the ResourceLoader interface and adds the function of accessing multiple resources at the same time.

public interface ResourcePatternResolver extends ResourceLoader {

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

// For example, use an ant-style path to match multiple resources under the path
Resource[] getResources(String locationPattern) throws IOException;

}
PathMatchingResourcePatternResolver is a direct implementation class of the ResourcePatternResolver interface. It is based on pattern matching. By default, AntPathMatcher is used for path matching. In addition to supporting the prefixes supported by ResourceLoader, it also supports &quot;classpath*&quot;. Check the source code below to see how it implements to-many access to a resource.

public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
     //First determine whether the resource path is a resource under the classpath (starting with &quot;classpath*:&quot;)
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        // Obtain PathMatcher through the getPathMatcher method, by default there is only one implementation class of AntPathMatcher
       // Use the isPattern method to determine whether the path allows multiple matching resources (the path contains &quot;*&quot; or &quot;?&quot;)
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // find all resources matching the path (ant style)
            return findPathMatchingResources(locationPattern);
        }
        else {
            //find by class loader
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    else {
        int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                locationPattern.indexOf(':') + 1);
        // Determine if the part after the resource path &quot;:&quot; contains &quot;*&quot; or &quot;?&quot;
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
        }
        else {
            // If there is no indication that it is a single resource, it is obtained through the ResourceLoader passed in from the constructor
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
        }
    }
}

Extension: View the source code and find that the ApplicationContext interface also inherits the ResourcePatternResolver interface, indicating that it also integrates access to single or multiple resources.

When Spring needs to access resources, it does not actually need to use the Resource implementation class directly, but to call the getResource method to obtain resources.
When the Resource instance is obtained through the ApplicationContext instance, it will be responsible for selecting the specific Resource implementation class. code show as below:
Resource res = ctx.getResource(“some/resource/path/myTemplate.txt);
Spring uses the same strategy as ApplicationContext to access resources. That is:

If ApplicationContext is FileSystemXmlApplicationContext, res is FileSystemResource instance;
If ApplicationContext is ClassPathXmlApplicationContext, res is the instance of ClassPathResource;
If ApplicationContext is XmlWebApplicationContext, res is a ServletContextResource instance.
That is to say, ApplicationContext will determine the specific resource access strategy, so as to separate the application from the specific resource access strategy, which reflects the advantages of the strategy pattern.

3.1.2 Set the application context initializer setInitializers
Introduction: initializers is an instance property in SpringApplication: List <ApplicationContextInitializer<?> &gt; initializers, each initailizer is an instance that implements the ApplicationContextInitializer interface. ApplicationContextInitializer is an interface provided in the Spring IOC container. The source code is as follows:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>{
    void initialize(C applicationContext);
}

Function: The function of the ApplicationContextInitializer interface is to do some initialization work when spring prepares theContext. When the prepareContext method is executed in the spring initialization process, it will call back all the implementations of the ApplicationContextInitializer interface through the applyInitializers method. Therefore, the setInitializers method is executed in the constructor of SpringApplication, which loads all the initialized ApplicationContextInitializer implementation classes into the collection inside SpringApplication.
Implementation: These initializers are the initialization classes that Spring Boot obtains the configuration from the local META-INF/spring.factories file and the META-INF/spring.factories file in the jar file (note that the configuration file is obtained here All configuration classes), and then pass loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
} method to obtain the complete name of all the implementation classes of the ApplicationContextInitializer interface, and finally obtain the ApplicationContextInitializer implementation class through the reflection mechanism.

Use: via getSpringFactoriesInstances(
ApplicationContextInitializer.class) method to get the implementation class

3.1.3 Set Listeners (setListeners)
The listeners member variable is an ApplicationListener<?> A collection of type objects. It can be seen that the same method as the member variable initializers is used to obtain the content of the member variable, except that the incoming type is changed from ApplicationContextInitializer.class to ApplicationListener.class.

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

This interface is based on the EventListener interface in the JDK and implements the observer pattern. For the implementation of the observer mode of the Spring framework, it defines that the event type of interest needs to be a subclass of the ApplicationEvent type, which is also inherited from the EventObject class in the JDK.

3.1.4 Infer the application entry class
This method constructs a runtime exception and obtains the name of the class where main() is located through the stack frame of the method named main in the exception stack.

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

3.2 run method
The source code is as follows:

public ConfigurableApplicationContext run(String… args) {

//1, timing monitoring class
StopWatch stopWatch = new StopWatch();
stopWatch.start();

// 2. Initialize the application context and exception report collection
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

// 3. Set the value of the system property java.awt.headless, the default value is: true (no graphical interface)
configureHeadlessProperty();

// 4. Create all Spring run listeners and emit events to start execution
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
    // 5. Initialize the default application parameter class
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
            
    // 6. Prepare the Spring environment according to SpringApplicationRunListeners and application parameters
    ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
    configureIgnoreBeanInfo(environment);
    
    // 7. Prepare the Banner printer - the ASCII art font printed on the console when Spring Boot is started
    Banner printedBanner = printBanner(environment);
    
    // 8. Create Spring context
    context = createApplicationContext();
    
    // 9. Prepare the exception reporter
    exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
            
    // 10. Spring context preprocessing
    prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
            
    // 11. Refresh the Spring context
    refreshContext(context);
    
    // 12. Spring context post processing
    afterRefresh(context, applicationArguments);
    
    // 13. Stop timing monitoring class
    stopWatch.stop();
    
    // 14. Output log record execution main class name and time information
    if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
    }
    
    // 15. Publish application context startup completion event
    listeners.started(context);
    
    // 16. Execute all Runner runners
    callRunners(context, applicationArguments);
}
catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, listeners);
    throw new IllegalStateException(ex);
}

try {
    // 17. Publish application context ready event
    listeners.running(context);
}
catch (Throwable ex) {
    handleRunFailure(context, ex, exceptionReporters, null);
    throw new IllegalStateException(ex);
}
// 18. Return to the application context
return context;

}
The following describes some important steps mentioned in the run method:

3.2.1 Timing monitoring class
The startup method is as follows:

public void start() throws IllegalStateException {
    start("");
}

public void start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start StopWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startTimeMillis = System.currentTimeMillis();
}

You can see that it passed an empty string to the current task as the task name, and then recorded the start time of the current Spring Boot application startup. And it will judge whether the current task name exists to ensure that the Spring Boot application does not start repeatedly.

3.2.2 Initialize application context and exception report collection
Here only an application context object context and an empty exception report collection are initialized. See below for specific purposes.

3.2.3 Setting System Properties
The method of configureHeadlessProperty is implemented as follows:

private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
            SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";

This method sets a system property named java.awt.headless. Looking at the source code, you can find that it sets the value of the property System.setProperty(), and its value comes from System.getProperty(). This is not surprising, because There are two overloaded methods of getProperty() in System, of which getProperty() has two methods: one-parameter and two-parameter. Here, the two-parameter method is called. This method will return a default value specified by the caller when it is not available. So here is first acquired and then set. The purpose of this setting is to ensure that the program is allowed to start even if no monitor is detected (the server does not need a monitor).

3.2.4 Create all Spring run listeners and emit events to start execution
The implementation is as follows:

//Get all SpringApplicationRunListeners listeners according to args
SpringApplicationRunListeners listeners = getRunListeners(args);

//This code should be familiar to everyone, get the SpringApplicationRunListeners extension
private SpringApplicationRunListeners getRunListeners(String[] args) {

Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));

}
by Class<?> [] types = new Class<?> []{SpringApplication.class, String[].class}; type to load the corresponding listener and create an instance of SpringApplicationRunListener

The following content is the same as the previous process of instantiating the initializer. Get the list of instance class names related to SpringApplicationRunListener.class from spring.factories through the getSpringFactoriesInstances method.

3.2.5 Initialize the default application parameter class
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
args are the command line arguments to start the Spring application, which can be accessed in the Spring application.
Such as: –server.port=9000

3.2.6 Prepare the Spring environment based on running listeners and application parameters
Create and configure the Environment to be used by the current SpringBoot application (including configuring the PropertySource and Profile to be used (its role is to specify the active configuration file, which can distinguish the environment to load different configurations)), and traverse the environmentPrepared() that calls all SpringApplicationRunListener method, the broadcast Environment is ready.
The source code is as follows:

private ConfigurableEnvironment prepareEnvironment(
        SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    //Get or create the environment (return directly if it exists, create one if it does not exist and return)
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //Configure the environment: configure PropertySources and activeProfiles
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //listeners environment preparation (that is, broadcasting the ApplicationEnvironmentPreparedEvent event).
    listeners.environmentPrepared(environment);
    //Bind the environment to SpringApplication
    bindToSpringApplication(environment);
    //If it is not a web environment, convert the environment to StandardEnvironment
    if (this.webApplicationType == WebApplicationType.NONE) {
        environment = new EnvironmentConverter(getClassLoader())
                .convertToStandardEnvironmentIfNecessary(environment);
    }
    //Configure the recursive dependency of PropertySources on itself
    ConfigurationPropertySources.attach(environment);
    return environment;
}

The role of prepareEnvironment: Load externalized configuration resources to the environment, including command line parameters, servletConfigInitParams, servletContextInitParams, systemProperties, sytemEnvironment, random, application.yml (.yaml/.xml/.properties), etc.; initialize the logging system.

3.2.7 Preparing the Banner Printer
This function is only for self-entertainment, do not do too much interpretation, just look at the source code.

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
            : new DefaultResourceLoader(getClassLoader()));
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
            resourceLoader, this.banner);
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

3.2.8 Creating a Spring context
The source code is as follows:

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = “org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext”;

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = “org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext”;

public static final String DEFAULT_CONTEXT_CLASS = “org.springframework.context.annotation.AnnotationConfigApplicationContext”;

protected ConfigurableApplicationContext createApplicationContext() {

// First determine whether there is a specified implementation class
    Class<?> contextClass = this.applicationContextClass;
    // if not, choose according to application type
    if (contextClass == null) {
        try {
            //Create a specific instance of ConfigurableApplicationContext by reflection based on the type of webApplicationType.
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    // Get an instance of the corresponding class through reflection
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

The logic of the createApplicationContext() method is relatively simple, with two branches:

Custom ApplicationContext implementation class
Match the corresponding ApplicationContext according to the current application type webApplicationType, whether it is a servlet, reactive or non-web application
3.2.9 Prepare exception reporter
The logic of this step is the same as that of instantiating initializers and listeners, by calling the getSpringFactoriesInstances method to obtain the configured exception class name and instantiate all exception handling classes.

3.2.10 Spring context preprocessing
The source code is as follows:

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    //Set the container environment, including various variables
    context.setEnvironment(environment);

    //Set the context's bean generator and resource loader
    postProcessApplicationContext(context);

    //Execute the ApplicationContextInitializer in the container (including spring.factories and custom instances)
    applyInitializers(context);

    //Trigger the contextPrepared event method of all SpringApplicationRunListener listeners
    listeners.contextPrepared(context);

    //Record startup log
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // Add bootstrap specific singleton bean

context.getBeanFactory().registerSingleton(“springApplicationArguments”,

applicationArguments);
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // load all resources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //Load our startup class and inject the startup class into the container
    load(context, sources.toArray(new Object[0]));

    //Trigger the contextLoaded event method of all SpringApplicationRunListener listeners
    listeners.contextLoaded(context);
}

This block will perform a preprocessing on the entire context, such as triggering the listener's response event, loading resources, setting the context, and so on.

3.2.11 Refresh the Spring context
The source code is as follows:

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            //Register a shutdown hook with the JVM runtime to close this context when the JVM shuts down
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

This piece does two main things:

Initialization of the entire IoC container through the refresh method (including Bean resource positioning, parsing, registration, etc.)
Via context.registerShutdownHook() (registers a shutdown hook with the JVM runtime, which closes the context when the JVM shuts down, unless it is already closed at the time.)
3.2.12 Spring context post processing
protected void afterRefresh(ConfigurableApplicationContext context,

    ApplicationArguments args) {

}
This method is not implemented, you can do some customized operations as needed.

3.2.13 Stop timing monitoring class
The source code is as follows:

public void stop() throws IllegalStateException {
    if (this.currentTaskName == null) {
        throw new IllegalStateException("Can't stop StopWatch: it's not running");
    }
    long lastTime = System.currentTimeMillis() - this.startTimeMillis;
    this.totalTimeMillis += lastTime;
    this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
    if (this.keepTaskList) {
        this.taskList.add(this.lastTaskInfo);
    }
    ++this.taskCount;
    this.currentTaskName = null;
}

This method is mainly to stop the operation of the timer listener and count some task execution information.

3.2.14 Output log record execution main class name and time information
The source code is as follows:

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);

}
Used to print main class information and time information.

3.2.15 Publish application context startup completion event
The source code is as follows:

public void started(ConfigurableApplicationContext context) {

for (SpringApplicationRunListener listener : this.listeners) {
    listener.started(context);
}

}
Execute the started method of all SpringApplicationRunListener implementations.

3.2.16 Execute all Runner runners
The Runner runner is used to perform some business initialization operations when the service is started, and these operations are only executed once after the service is started.
Spring Boot provides ApplicationRunner and CommandLineRunner two service interfaces CommandLineRunner, ApplicationRunner
Compared:

Same point

Both are executed after the service startup is complete, and only once.
Both can get the command line parameters of the application.
The execution timing of the two is consistent (you can implement custom execution priority through Ordered-related interfaces or annotations.).
difference

Although both obtain the command line parameters of the application, ApplicationRunner obtains the encapsulated ApplicationArguments object, and CommandLine obtains the sourceArgs property in ApplicationArguments (List<String> ), which is a list of raw parameter strings (list of command line parameters).
The source code is as follows:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();

// Get the ApplicationRunner implementation class from the Spring container runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// Get the CommandLineRunner implementation class from the Spring container runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());

//sort
    AnnotationAwareOrderComparator.sort(runners);
    //callback
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

ApplicationRunner or CommandLineRunner both execute a business initialization code after the application is started, and the effect is similar. Since the method parameter of ApplicationRunner is the ApplicationArguments object, it is more convenient to use, so it is more recommended.

3.2.17 Publish application context ready event
The source code is as follows

public void running(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.running(context);
    }
}

Triggers the running event method of all SpringApplicationRunListener listeners