SpringApplication.run execution flow

SpringApplication "templated" the process started by a typical Spring application (here is a verb). In the absence of special requirements, the default templated execution process can meet the needs, but it does not matter if there are special needs. SpringApplication is suitable. The process node has opened a series of different types of extension points, through which we can extend the startup and shutdown process of the Spring Boot program.

SpringApplication will load application.properties files from the following locations and add them to the Spring Environment:

  1. / Config subdirectory under the current directory.
  2. Current directory.
  3. / config package under classpath.
  4. classpath root path (root).

The list is sorted by priority (attributes defined under a path with a higher position in the list will override those with a lower position).

Note You can use YAML ('.yml') file instead of '.properties'.

If you do not like to use application.properties as the configuration file name, you can switch to another name by specifying the spring.config.name environment property, or you can use the spring.config.location environment property to refer to a clear path (directory location or file path The list is separated by commas). 

$ java -jar myproject.jar --spring.config.name=myproject

 or

$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

The most "superficial" extension or configuration is the customization method that SpringApplication opens through a series of setters. For example, there is only one sentence in the main method of our previous startup class:

SpringApplication.run(DemoApplication.class,args);

But if we want to extend the startup behavior through a series of setting methods of SpringApplication, we can proceed as follows:

public class DemoApplication {
    public static void main(String[] args) {
        // SpringApplication.run(DemoConfiguration.class, args);
        SpringApplication bootstrap = new SpringApplication(Demo - Configuration.class);
        bootstrap.setBanner(new Banner() {
            @Override
            public void printBanner(Environment environment, Class<?> aClass, PrintStream printStream) {
                // 比如打印一个我们喜欢的ASCII Arts字符画
            }
        });
        bootstrap.setBannerMode(Banner.Mode.CONSOLE);
        // 其他定制设置...
        bootstrap.run(args);
    }
}

The easiest way to set up a custom banner is to put the ASCII Art character drawing in a resource file, and then load it through ResourceBanner: 

bootstrap.setBanner(new ResourceBanner(new ClassPathResource("banner.txt"))); 

In most cases, SpringApplication has provided good default settings, so we will not explore these surface layers, because it is our ultimate goal to explore the things below the surface layer.

In-depth exploration of the SpringApplication execution process

The implementation of SpringApplication's run method is the main line of our journey. The main process of this method can be summarized as follows:

1) If we are using the static run method of SpringApplication, then, this method first needs to create a SpringApplication object instance , And then call the run method of the created SpringApplication instance. When the SpringApplication instance is initialized, it will do a few things in advance:

according to whether there is a feature class (org.springframework.web.context.ConfigurableWebApplicationContext) in the classpath to decide whether to create an ApplicationContext type for Web applications, or An ApplicationContext type used by a standard Standalone application should be created.

Use SpringFactoriesLoader to find and load all available ApplicationContextInitializer in the application classpath.

Use SpringFactoriesLoader to find and load all available ApplicationListener in the application classpath.

Infer and set the definition class of the main method.

2) After the initialization of the SpringApplication instance is completed and the settings are completed, the logic of the run method begins. At the beginning of the method execution, first traverse all SpringApplicationRunListeners that can be found and loaded through the SpringFactoriesLoader, call their started () method, and tell these SpringApplicationRunListeners, "Hey, the SpringBoot application is about to start!".

3) Create and configure the Environment to be used by the current Spring Boot application (including configuring the PropertySource and Profile to be used).

4) Traverse the environmentPrepared () method of all SpringApplicationRunListener calls and tell them: "The Environment used by the current Spring Boot application is ready!".

5) If the showBanner property of SpringApplication is set to true, print the banner (SpringBoot version 1.3.x, here should be based on Banner.Mode to determine the printing behavior of the banner). The logic of this step can actually be ignored. I think the only purpose is to "just for fun".

6) According to whether the user has explicitly set the applicationContextClass type and the inference results of the initialization phase, decide what type of ApplicationContext should be created for the current Spring Boot application and create it, then decide whether to add ShutdownHook according to the conditions, decide whether to use a custom BeanNameGenerator, and decide whether to Use a custom ResourceLoader. Of course, the most important thing is to set the Environment prepared before to the created ApplicationContext.

7) After the ApplicationContext is created, SpringApplication will use the Spring-FactoriesLoader again to find and load all available ApplicationContext-Initializers in the classpath, and then traverse the initialize (applicationContext) method of these ApplicationContextInitializers to further process the already created ApplicationContext.

8) Traverse the contextPrepared () method of all SpringApplicationRunListeners and notify them: "The ApplicationContext used by the SpringBoot application is ready!"

9) The core step is to load all the configurations obtained through @EnableAutoConfiguration and other forms of IoC container configuration. To the ApplicationContext that has been prepared.

10) Traverse and call the contextLoaded () method of all SpringApplicationRunListeners to inform all SpringApplicationRunListeners that the ApplicationContext is "filled"!

11) Call the refresh () method of ApplicationContext to complete the last procedure available to the IoC container.

12) Find out whether CommandLineRunner is registered in the current ApplicationContext, and if so, traverse and execute them.

13) Under normal circumstances, iterate through the finished () method of SpringApplicationRunListener and tell them: "Get it!" (If an exception occurs during the entire process, all SpringApplicationRunListener's finished () methods are still called, but in this case, the exception information will be passed in for processing).

At this point, a complete SpringBoot application is launched!

The whole process looks very lengthy, but many of them are extension points of some event notifications. If we ignore these logics for a while, then the logic of the entire SpringBoot application startup can be compressed into extremely streamlined steps as shown in Figure 1. Show.

                          Schematic diagram of the SpringBoot application startup steps
                                                Figure 1 Spring Boot application startup schematic diagram


We can find out from the comparison before and after, in fact, these various extension points provided by SpringApplication are almost "huobinzhu", occupying most of the "jiangshan" of a Spring application startup logic, except for initializing and preparing the ApplicationContext, most of the remaining work is This is done through these extension points, so we will analyze each extension point one by one.

SpringApplicationRunListener

SpringApplicationRunListener is a listener that only receives notifications of events at different execution time during the execution of the main method of the Spring Boot application:

public interface SpringApplicationRunListener {
    void started();
    void environmentPrepared(ConfigurableEnvironment environment);
    void contextPrepared(ConfigurableApplicationContext context);
    void contextLoaded(ConfigurableApplicationContext context);
    void finished(ConfigurableApplicationContext context, Throwable exception);
}

For us, there are basically no common scenarios where you need to implement a Spring-ApplicationRunListener yourself. Even if SpringBoot only implements an org.spring-framework.boot.context.event.EventPublishingRunListener by default, it is used to publish at different points in SpringBoot startup. Different application event types (ApplicationEvent), if any ApplicationListener is interested in these application events, they can be received and processed.

Assuming that we really have a scene that needs to customize a SpringApplicationRunListener implementation, then one thing to note is that any constructor of a SpringApplicationRunListener implementation class (Constructor) needs to have two construction parameters, and the type of one construction parameter is our org.springframework. boot.SpringApplication, the other is String [] of the args parameter list: 

public class DemoSpringApplicationRunListener implements SpringApplicationRunListener {
    @Override
    public void started() {
        // do whatever you want to do
    }
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        // do whatever you want to do
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        // do whatever you want to do
    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        // do whatever you want to do
    }
    @Override
    public void finished(ConfigurableApplicationContext context, Throwable exception) {
        // do whatever you want to do
    }
}

After that, we can use the rules set by SpringFactoriesLoader to configure the following configuration in the META-INF / spring.factories file under the classpath of the current SpringBoot application: 

org.springframework.boot.SpringApplicationRunListener=\com.keevol.springboot.demo.DemoSpringApplicationRunListener 

Then SpringApplication will call it at runtime!

ApplicationListener

ApplicationListener is actually an old face. It belongs to a framework implementation of the Listener mode implemented in Java by the Spring framework. The only thing worth highlighting here is that for the first time to contact SpringBoot, but not too much contact with the Spring framework developers. That said, the name may be confused with SpringApplicationRunListener.

We will not introduce too much about ApplicationListener. If you are interested, please refer to the relevant materials and books of Spring framework.

If we want to add custom ApplicationListener for SpringBoot application, there are two ways:

  • Add one or more custom ApplicationListeners through SpringApplication.addListeners (..) or SpringApplication.setListeners (..)
  • With the help of the SpringFactoriesLoader mechanism, add configuration in the META-INF / spring.factories file (the following code is the ApplicationListener configuration registered for SpringBoot by default).

org.springframework.context.ApplicationListener=
\org.springframework.boot.builder.ParentContextCloserApplicationListener,
\org.springframework.boot.cloudfoundry.VcapApplicationListener,
\org.springframework.boot.context.FileEncodingApplicationListener,
\org.springframework.boot.context.config.AnsiOutputApplicationListener,
\org.springframework.boot.context.config.ConfigFileApplicationListener,
\org.springframework.boot.context.config.DelegatingApplicationListener,
\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicat-ionListener,
\org.springframework.boot.logging.ClasspathLoggingApplicationListener,
\org.springframework.boot.logging.LoggingApplicationListener 

 About ApplicationListener, we say this.

ApplicationContextInitializer

ApplicationContextInitializer is also the original concept of the Spring framework. The main purpose of this class is to allow us to do further settings or processing on the ConfigurableApplicationContext instance before the ApplicationContext of the ConfigurableApplicationContext type (or subtype) is refreshed.

Implementing an ApplicationContextInitializer is simple because it only has one method to implement:

public class DemoApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // do whatever you want with applicationContext,
        // e.g.
        applicationContext.registerShutdownHook();
    }
}

However, under normal circumstances, we basically do not need to customize an ApplicationContext-Initializer, even though the SpringBoot framework only registers three implementations by default: 

org.springframework.context.ApplicationContextInitializer=
\org.springframework.boot.context.ConfigurationWarningsApplication-ContextInitializer,
\org.springframework.boot.context.ContextIdApplicationContextInitia-lizer,
\org.springframework.boot.context.config.DelegatingApplicationContex-tInitializer 

If we really need to customize an ApplicationContextInitializer, as long as it is configured as above, it can be configured through the SpringFactoriesLoader mechanism or set through SpringApplication.addInitializers (..)

CommandLineRunner

CommandLineRunner is a good extension interface, not the original "baby" of the Spring framework, it belongs to the SpringBoot application-specific callback extension interface. The source code is as follows: 

public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

CommandLineRunner needs everyone's attention in fact, there are actually two points:

1) The execution time of all CommandLineRunner is after the Application-Context of the SpringBoot application is completely initialized and started to work (it can be regarded as the last step before the main method execution is completed).

2) As long as any Command-LineRunner exists in the ApplicationContext of the current Spring Boot application, it will be loaded and executed (whether you manually register the CommandLineRunner to the IoC container or automatically scan it).

Similar to several other extension point interface types, it is recommended that the CommandLineRunner implementation class use @ org.springframework.core.annotation.Order to annotate or implement the org.springframework.core.Ordered interface to facilitate adjustment of their execution order, which is actually Very important, we do not want the improperly ordered CommandLineRunner implementation class to block the execution of other CommandLineRunners later. 

Published 203 original articles · won praise 6 · views 4489

Guess you like

Origin blog.csdn.net/weixin_42073629/article/details/105462520