preface

I’ve been writing the Spring Boot column for more than 50 days, and the first 20 chapters cover everything from basic applications to advanced integration.

Today begins the stage of the introduction of the underlying source code, the relative content is a little deeper, the author is also introduced as far as possible easy to understand, hierarchical point. I believe that READ my Mybatis column articles know, as long as follow the author’s steps, method step by step research, in fact, the source is not difficult.

This article took four days to elaborate, and strive to introduce the easy to understand, after all, the source relatively more difficult, I hope to explain to help readers through the author split.

If you haven’t read the author’s first 20 articles, click there

Source code version

The author Spring Boot is based on 2.4.0. Each version has some changes, and readers try to be consistent with me in case there are some discrepancies in the source code.

Where to start?

I believe that many people have tried to read the source of Spring Boot, but have not found the appropriate method. That’s because you don’t know much about the various components and mechanisms of Spring Boot. It’s like looking for a needle in a haystack.

As for where to start, of course the main bootclass, which is annotated with @SpringBootApplication and has a main() method, looks like this:

@SpringBootApplication
public class AnnotationDemoApplication {
    public static void main(String[] args) { SpringApplication.run(AnnotationDemoApplication.class, args); }}Copy the code

No more words, DEBUG wait, don’t be afraid, get it……..

How to split source code?

The static run() method in SpringApplication is not implemented in one step. The source code is as follows:

//org.springframework.context.ConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class
       [] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
Copy the code

There are obviously two steps, creating the SpringApplication and executing the Run () method, which are described in the following sections.

How do I create SpringApplication?

Create the new object, DEBUG follows the code, and finally execute the SpringApplication constructor as shown below:

As noted in the figure above, the reuse of the creation process is actually divided into three phases (②, ③, ④), and what is done in each phase is described below.

Setting the Application Type

This process is very important and directly determines the type of the project. There are three types of application types, all in the WebApplicationType enumeration class, as follows:

  1. NONE: As the name implies, nothing, normal flow goes, no extra startupThe web container, such asTomcat.
  2. SERVLETBased on:servletThe Web program needs to start inlineservletA Web container, for exampleTomcat.
  3. REACTIVEBased on:reactiveWeb program, need to start embeddingreactiveWeb container, the author does not know very much, inconvenient to say more.

The basis of judgment is very simple, is to load the corresponding class, such as the load of DispatcherServlet will determine the Web program is Servlet. The source code is as follows:

static WebApplicationType deduceFromClasspath(a) {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if(! ClassUtils.isPresent(className,null)) {
				returnWebApplicationType.NONE; }}return WebApplicationType.SERVLET;
	}
Copy the code

Here I introduce spring-boot-starter-Web, which is definitely a Servlet web application.

Set the Initializer

Initializer ApplicationContextInitializer is a good thing for the IOC container refresh before initialize some components, such as ServletContextApplicationContextInitializer.

So how do you get the initializer? Follow the code in the figure above to enter the method in the SpringApplication as shown below:

The first step is to get the name of the initializer, which must be the full class name. The full source must be in the loadFactoryNames() method. Final call is # SpringFactoriesLoader loadSpringFactories () method.

LoadSpringFactories () method is no longer a detailed explanation, is actually from the classpath meta-inf/spring. The value of the load ApplicationContextInitializer in factories.

The values in the spring.factories file with spring-boot-autoconfigure look like this:

The initializers in the figure above are just a few, because there is more than one spring.Factories file.

The following is an example of the initializer I injected in my demo, but not in a real project.

It also tell us the custom only need to implement a ApplicationContextInitializer interface, in the spring. The factories file Settings.

Setting a Listener

The concept of ApplicationListener has been around since Spring. It is used to listen for specific applicationevents, such as IOC container refreshes, container closures, and so on.

Spring Boot extends ApplicationEvent to build the abstract SpringApplicationEvent class, which is mainly used for events triggered during Spring Boot startup, such as program startup, program startup, etc. The diagram below:

How do I get a listener? Know from the source and actually initializer (ApplicationContextInitializer) is the same method, from the meta-inf/spring. The same factories file access.

The values in the spring.factories file with spring-boot-autoconfigure look like this:

There is more than one spring.factories file and more than one listener.

Some of the listeners injected in the author’s demo are shown below:

conclusion

The SpringApplication is built to prepare for the run() method to start. There are only a few lines of code in the constructor. The most important parts are setting the application type, initializer, and listener.

Note: Both the initializer and the listener here have to be in a spring.Factories file to be loaded at this step, otherwise it won’t work, so the IOC container hasn’t been created yet, and it won’t work even if you inject it into the IOC container.

The author draws a simple execution flow chart for reference only, as follows:

Execute the run() method

The above analysis of the SpringApplication build process, all set, now it is time to launch the process.

The author will start the process is divided into 8 steps according to the source code, which will be introduced one by one below.

1. Obtain and start the running process listener

SpringApplicationRunListener the listener and ApplicationListener is different, it is used to monitor application startup process, the various methods of the interface meaning is as follows:

public interface SpringApplicationRunListener {

    // This method is called as soon as the run() method begins execution and can be used to do some work at the earliest stage of initialization
    void starting(a);
    // This method is called when the Environment is built and before ApplicationContext is created
    void environmentPrepared(ConfigurableEnvironment environment);
    This method is called when ApplicationContext is built
    void contextPrepared(ConfigurableApplicationContext context);
    This method is called after ApplicationContext has finished loading but has not been refreshed
    void contextLoaded(ConfigurableApplicationContext context);
    This method is called after ApplicationContext is refreshed and started, and before CommandLineRunners and ApplicationRunner are called
    void started(ConfigurableApplicationContext context);
    // This method is called before the run() method completes execution
    void running(ConfigurableApplicationContext context);
    // This method is called when the application fails to run
    void failed(ConfigurableApplicationContext context, Throwable exception);
}
Copy the code

How do I get run listeners?

In the SpringApplication#run() method, the source code is as follows:

// Get the listener from spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
Copy the code

The loadFactoryNames() method is called to retrieve the values from the Spring.Factories file.

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Copy the code

Eventually injection is EventPublishingRunListener the implementation class, create an instance process must be through reflection, so we see the construction method, the diagram below:

The running inside the listener has an event broadcast (SimpleApplicationEventMulticaster), is mainly used to broadcast a particular event (SpringApplicationEvent) to trigger a specific listener ApplicationListener.

In each method is used to trigger the SpringApplicationEvent EventPublishingRunListener different subclasses.

How do I start a run listener?

In the SpringApplication#run() method, the source code is as follows:

// Execute the starting() method
listeners.starting(bootstrapContext, this.mainApplicationClass);
Copy the code

Perform SpringApplicationRunListeners starting () method, with inside actually very simple, traverse perform the operation of the above for listeners, there’s only one EventPublishingRunListener. So its starting() method is executed, and the source code looks like this:

The logic in the source code is simple; it simply executes the multicastEvent() method, broadcasting the ApplicationStartingEvent event. What’s interesting about the multicastEvent() internal method is that it essentially iterates through the implementation class ApplicationListener to find a listener that listens for ApplicationStartingEvent, Execute the onApplicationEvent() method.

conclusion

This step essentially broadcasts the ApplicationStartingEvent event to trigger the ApplicationListener that listens for the event.

So if you customize the ApplicationListener and listen for the ApplicationStartingEvent event, the listener will be fired.

2. Environment construction

This step is used to load the system configuration and the user’s custom configuration (application.properties). The source code is as follows, in the run() method:

ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Copy the code

PrepareEnvironment methods the internal radio ApplicationEnvironmentPreparedEvent events, source code is as follows:

Environment to build the step loading system environment configuration, user-defined configuration and broadcast the ApplicationEnvironmentPreparedEvent events, triggering the listener.

3. Create IOC containers

The source code is in the run() method as follows:

context = createApplicationContext();
Copy the code

Follow up the code, the real execution is ApplicationContextFactory method, the diagram below:

, depending on the type of webApplicationType decided to create very clearly, I here is the servlet, thus creating AnnotationConfigServletWebServerApplicationContext.

This step just creates the IOC container, nothing else.

4. Pre-processing of IOC containers

This is the quintessential step in preparing the container before refreshing it: inject the startup class into the container to set the stage for automated configuration. The source code is as follows:

prepareContext(context, environment, listeners, applicationArguments,printedBanner);
Copy the code

PrepareContext () source code: prepareContext()

As can be seen from the figure above, there are many steps, and several key points will be introduced in detail below.

Call the initializer

The initializer set during the SpringApplication build, from the spring.factories value. The execution process is very simple, traversing the execution, the source code is as follows:

The custom ApplicationContextInitializer in meta-inf/spring. Factories, also will be called at this time.

Load the startup class and inject the container

This step is to load the main startup class into the IOC container as an entry point for subsequent automatic configuration.

GetAllSources () is taken from the set primarySources in which the main launcher class is placed during SpringApplication construction, as shown below:

This is the main startup class, of course, your project may have more than one, the next is to load it into the IOC container, source code as follows:

load(context, sources.toArray(new Object[0]));
Copy the code

Following code inside, actually the main logic in BeanDefinitionLoader. The load () method, the diagram below:

Load the main startup class into beanDefinitionMap, which will serve as an entry point for enabling automatic configuration, as described in the following sections.

Two broadcast events

This step involves two broadcast events, is ApplicationContextInitializedEvent and ApplicationPreparedEvent respectively, the corresponding source code is as follows:

listeners.contextPrepared(context);
load(context, sources.toArray(new Object[0]));
Copy the code

5. Refresh the container

Refreshing the container is completely Spring’s function, such as initializing resources, initializing context broadcasters, etc., this will not be introduced in detail, if you are interested in Spring source code.

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    // Call the refresh() method in the created container applicationContext
    ((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh(a) throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        /** * Refresh context */
        prepareRefresh();

        /** * Initializes the BeanFactory and parses the XML, the same as the previous XmlBeanFactory operation, */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        /** * Prepare the BeanFactory for the context, that is, populate the various functions of the BeanFactory, such as common annotations@Autowired @QualifierSuch as * * add ApplicationContextAwareProcessor processor in di ignore the interface implementation * Aware, such as EnvironmentAware, ApplicationEventPublisherAware * registered dependence, Such as a bean attribute contains ApplicationEventPublisher (the beanFactory), will the beanFactory instance injection in * /
        prepareBeanFactory(beanFactory);

        try {
            /** * provides additional handling for subclass overrides, i.e. subclasses handle custom BeanFactoryPostProcess */
            postProcessBeanFactory(beanFactory);

            / * * * to activate a variety of the BeanFactory processor, including BeanDefinitionRegistryBeanFactoryPostProcessor and ordinary spring BeanFactoryPostProcessor * To perform the corresponding postProcessBeanDefinitionRegistry method and postProcessBeanFactory * /
            invokeBeanFactoryPostProcessors(beanFactory);

            /** * Register the BeanPostProcessor, not BeanFactoryPostProcessor, of the intercepting Bean. The corresponding method */ is executed on the bean's instantiation
            registerBeanPostProcessors(beanFactory);

            /** * Initializes resource files in the context, such as processing of internationalization files, etc. */
            initMessageSource();

            / * * * initializes the context event broadcast, and into the applicatioEventMulticaster, such as ApplicationEventPublisher * /
            initApplicationEventMulticaster();

            /** * Initializes other beans for subclass extensions */
            onRefresh();

            /** * Find the listener bean among all the beans and register it with the broadcaster */
            registerListeners();

            /** * Set converter * registers a default property value parser * freezes all bean definitions, indicating that the registered bean definitions cannot be modified or further processed */ Initializes the remaining non-lazy beans, i.e. initializes non-lazy loaded beans */
            finishBeanFactoryInitialization(beanFactory);

            /** * Publish ContextRefreshedEvent events through Spring's event publishing mechanism to ensure that the corresponding listener does further processing. These classes implement ApplicationListener
      
       . This is where the execution of these classes is triggered (the onApplicationEvent method is executed). Spring's built-in events are ContextClosedEvent, ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, RequestHandleEvent * When initialization is complete, notify the lifeCycleProcessor of the refresh process and issue a ContextRefreshEvent to notify others */
      
            finishRefresh();
        }

        finally{ resetCommonCaches(); }}}Copy the code

6. Post-processing of IOC containers

An extension method, source code as follows:

afterRefresh(context, applicationArguments);
Copy the code

The default value is null, and can be overridden if there are custom requirements, such as printing some start end logs, etc.

7. Send an event to end execution

Also is EventPublishingRunListener the listener, radio ApplicationStartedEvent events.

But unlike the previous times, the broadcast event is not broadcast to listeners in the SpringApplication (listeners retrieved from the Spring.Factories file during the build). So listeners injected in the IOC container (with @Component, etc.) can also work. The first few events are valid only for listeners set up in the Spring.Factories file.

The started() method is displayed as follows:

Here not broadcast device SimpleApplicationEventMulticaster broadcast events, but instead USES ConfigurableApplicationContext directly publish event in the IOC container.

8. Runners

Spring Boot provides two runners that allow you to customize additional operations, CommandLineRunner and ApplicationRunner, which are described in more detail later in this article.

The source code for the call is as follows:

callRunners(context, applicationArguments);
Copy the code

Follow up code, in fact, the real execution is the following method:

The logic is simple: get from the IOC container and iterate over the call.

conclusion

The Spring Boot startup process is relatively simple, and the author subdivides it into the above eight steps, hoping to help readers understand it. The flowchart is as follows:

conclusion

This concludes the Spring Boot startup process, focusing on the eight steps performed by the run() method and the execution points of the events, initializers, listeners, and other components.

The author of every article is very carefully, this source analysis took three days to elaborate, and strive to explain the easy to understand, I hope to help you.