Analysis of SpringBoot startup configuration principles

Table of contents

1. Overall overview

(1) Basic overall preliminary analysis

(2) Analysis of the overall process diagram from the perspective of startup

2. Analysis of SpringApplication construction process

(1) Verify that the main configuration class is not empty and save the main class

(2) Infer the type of project

(3) Initialization initializers

(4) Load related listeners

(5) Determine the ApplicationClass main program

3. Analysis of SpringApplication startup process

(1) The monitor monitors the startup of the container and performs graphical page processing

(2) The listener SpringApplicationRunListeners turns on listening

(3) environmentPrepared environment preparation processing

(4) Banner printing

(5) Create Spring application context

(6) Spring application context preparation phase

(7) Spring application context refresh phase

(8) Spring application context closing stage

(9) Callback work processing

(10) SpringApplication startup exception handling

4. SpringBoot automatic configuration analysis

(1) Analysis of automatic assembly principles

(2) Conditional automatic assembly

(3) Example of automatic configuration principle: HttpEncodingAutoConfiguration (HTTP encoding automatic configuration)


1. Overall overview

(1) Basic overall preliminary analysis

Spring Boot is a framework for building standalone, production-grade Spring applications that provides automated configuration and the principle of convention over configuration. Before understanding the startup configuration principle of Spring Boot, we need to understand several key concepts.

First, Spring Boot uses a convention-based automatic configuration mechanism . It automatically configures the various components of a Spring application based on the dependencies used by the application by looking for specific configuration files and classes under the classpath. This can greatly simplify the work of developers and reduce the need for manual configuration.

Secondly, Spring Boot uses the conditional configuration mechanism. This means that the application of the configuration depends on whether a set of conditions are met. Conditions can be based on a variety of factors, such as the presence of a specific class in the classpath, the existence of a specific bean, etc. Through conditional configuration, Spring Boot can dynamically configure according to different environments and needs.

The startup configuration principle of Spring Boot can be summarized as follows:

  1. During the startup process, Spring Boot loads and parses the application's configuration files, including application.properties or application.yml files, etc. Various properties and configuration information can be defined in these files, such as database connections, log levels, etc.
  2. Spring Boot will automatically scan specific packages under the classpath to find classes with specific annotations, such as @SpringBootApplication. This annotation identifies the entry point of a Spring Boot application.
  3. Based on the properties in the configuration file and the conditional configuration mechanism, Spring Boot automatically configures various components of the application, including database connection pools, message queues, web servers, etc. If you need custom configuration, you can use special annotations or write a custom configuration class.
  4. When the application starts, Spring Boot will initialize the Spring container and perform corresponding initialization work according to the configuration. This includes creating and managing beans, handling dependency injection, etc.

In general, Spring Boot's startup configuration principle is based on automated conventions and conditional configuration mechanisms. It simplifies the application configuration process and provides flexibility and ease of use through steps such as reading configuration files, scanning annotations, and automatically configuring components.

(2) Analysis of the overall process diagram from the perspective of startup

Each SpringBoot program has a main entrance main method. SpringApplication.run() is called in main to start the entire SpringBoot program. The class in which this method is located needs to be annotated with @SpringBootApplication, for example, as follows

package org.zyf.javabasic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * 描述:启动入口类
 *
 * @author yanfengzhang
 * @date 2019-12-19 18:11
 */
@SpringBootApplication
@ComponentScan(basePackages = {"org.zyf.javabasic"})
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableSwagger2
public class ZYFApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ZYFApplication.class, args);
}

Among them, @SpringBootApplication is expanded and analyzed:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

@SpringBootApplication includes three annotations with the following functions:

  • @SpringBootConfiguration (internally @Configuration): The annotated class is equal to the spring XML configuration file (applicationContext.xml), assembles all bean transactions, and provides a spring context.
  • @ComponentScan: Component scanning, which can automatically discover and assemble beans (such as @Component and @Configuration). By default, it scans all files in the package path where the class in the run method of SpringApplication is located.
  • @EnableAutoConfiguration: Activate SpringBoot auto-wiring features

Now expand the main method of the main entrance to give an overall flow chart analysis.

2. Analysis of SpringApplication construction process

Enter the run method in main, create a SpringApplication instance, configure some basic environment variables, resources, constructors, and listeners, and enter the SpringApplication parameterized constructor

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

Note: In this constructor, the work done is to load the relevant classes (mainly initializer and listener) into the container, and it is not executed.

(1) Verify that the main configuration class is not empty and save the main class

(2) Infer the type of project

The analysis of entering the corresponding method is as follows:

The inferred project type may be reactive, none, or servlet. The default is servlet type. The logic of using the class loader to determine the type is as follows

type

judge the situation

reactive

Spring WebFlux's DispatcherHandler exists, but Spring MVC's DispatcherServlet does not exist

none

Neither exists

servlet

All remaining situations

(3) Initialization initializers

First enter the ApplicationContextInitializer interface. This interface has only one method initialize.

package org.springframework.context;

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

View the classes that implement this interface for analysis

Taking the SharedMetadataReaderFactoryContextInitializer implementation class as an example, jump to the corresponding jar package, and you can see that spring.factories specifies the corresponding implementation class in the key ApplicationContextInitializer, for example:

Enter SharedMetadataReaderFactoryContextInitializer, which implements the ApplicationContextInitializer interface and implements the initialize method inside

Now go back to the beginning and look at the getSpringFactoriesInstance() method, the core of which is the loadFactoryNames() method

Entering the loadFactoryNames() method, you can see that the relevant acquisition content of ApplicationContextInitializer is directly obtained and saved from the file "META-INF/spring.factories"

(4) Load related listeners

First enter the ApplicationListener interface. This interface has only one method onApplicationEvent.

package org.springframework.context;

import java.util.EventListener;

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

View the classes that implement this interface for analysis

Take the BackgroundPreinitializer implementation class as an example. Jump to the corresponding jar package. You can see that spring.factories specifies the corresponding implementation class in the key ApplicationListener:

 Enter BackgroundPreinitializer, which implements ApplicationListener and implements onApplicationEvent inside

Methods as below:

Now go back to the beginning and look at the getSpringFactoriesInstance() method. The processing flow is the same as above, that is, all ApplicationListeners configured in META-INF/spring.factories are still found from the class path.

(5) Determine the ApplicationClass main program

Enter the deduceMainApplicationClass method

3. Analysis of SpringApplication startup process

The SpringBoot startup solution includes the monitoring module of the startup process, the loading configuration environment module, the core creation context environment module, and subsequent closing callbacks.

(1) The monitor monitors the startup of the container and performs graphical page processing

(2) The listener SpringApplicationRunListeners turns on listening

Start analyzing ApringApplicationRunListeners directly , which contains a collection of SpringApplicationRunListeners. The starting method is to traverse the listeners and call the starting method for each listener.

SpringApplicationRunListener and ApplicationListener are both event listeners in SpringBoot, but the events they listen to and the triggering timing are different. The differences are as follows:

  1. The events monitored are different.
    SpringApplicationRunListener mainly listens to various events during SpringApplication runtime, such as application startup, application startup failure, application startup completion and other events. The ApplicationListener mainly listens to various events in the Spring container, such as Bean loading completion, context refresh completion and other events.
  2. The triggering timing is different.
    SpringApplicationRunListener starts working when SpringApplication starts, and can receive various events such as application startup, application startup failure, and application startup success. The ApplicationListener can only start working after the Spring container is started, and it listens to various events in the Spring container.
  3. Different usage scenarios
    In actual applications, SpringApplicationRunListener is mainly used to monitor the startup process of SpringApplication, such as performing certain operations before and after the application starts, listening to application startup failure events and taking corresponding operations, etc. The ApplicationListener is used to listen to various events in the Spring container, such as performing corresponding operations after the Bean is loaded, updating some status after the context refresh is completed, etc.

In short, although SpringApplicationRunListener and ApplicationListener are both event listeners in SpringBoot, the events they listen to, triggering timing, usage scenarios, etc. are different. We need to choose the appropriate listener to complete the application based on the specific application needs. event handling.

Take a look at the SpringApplicationRunListener class before entering

package org.springframework.boot;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

The corresponding method is explained as follows

Listening method

Operation phase description

SpringBoot starting version

contextLoaded(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext has completed loading , but has not started yet; notify the listener that ApplicationContext has completed IoC configuration

1.0

contextPrepared(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext is ready : notify the listener that the ApplicationContext has been created and initialized.

1.0

environmentPrepared(ConfigurationEnvironment)

The ConfigurationEnvironment is ready to allow it to be adjusted

1.0

failed(ConfigurationApplicationEnvironment,Throwable)

Spring application failed to run

2.0

running(ConfigurationApplicationEnvironment)

Spring application is running

2.0

started(ConfigurationApplicationEnvironment)

ConfigurableApplicationContext has been started and SpringBean has been initialized.

2.0

starting()

Execute immediately when the run method is executed: notify the listener and SpringBoot starts execution

1.0

In general, the application's listener SpringApplicationRunListeners is created and start() is called to start listening . First, all the listeners are obtained in getRunListeners, and then starting is turned on.

(3) environmentPrepared environment preparation processing

Analyze this code and enter the prepareEnvironment method to prepare the environment. You can see the following:

First, create an environment ConfigurationEnvironment (get it if it exists, create it if it doesn’t)

Go back to the place returned by this method and set the environment through this.configureEnvironment, followed by the following:

After the environment is configured, the environmentPrepared function of SpringApplicationRunListener is called back and enters this method:

You can see that the listener is notified during environmentPrepared environment preparation, and Environment preparation is completed.

Back to the beginning, you can see that after the environment preparation is completed, the environment is bound to the program through bindToSpringApplication.

(4) Banner printing

Print the corresponding banner information

That is, the modified part after startup, as shown below

In practice, the image can be replaced by changing the image. You only need to add banner.txt information to resources, for example, as follows:

You can continue to click on the corresponding code analysis for the principles. The replacement ideas will be explained in this follow-up.

(5) Create Spring application context

Creating an application context is the IOC process. The IOC container is the core that drives the overall SpringBoot application component. Enter this method:

The creation here is to create an IOC container based on the web application type inferred by SpringApplication during the construction phase. The IOC container is the content returned by run.

(6) Spring application context preparation phase

The prepareContext method associates important components such as listeners, environment, applicationArguments, and banners with the context object, further configures the context object, and enters the detailed analysis of this method:

You can see that the environment and ApplicationContext just generated are first saved respectively. The next applyInitializers method is to perform initialization and enter this method.

The method internally traverses all initializers, and then calls back all the initialize methods in sequence (these initializers were added when springboot first started when constructing new springApplication). After setting the current environment and completing initialization, the contextPrepared method of all Linsteners is called back.

Next, register the command line parameters and banner to the IOC container, as follows:

After all operations are completed, this method calls back the contextLoaded method of Listeners, as above.

(7) Spring application context refresh phase

In this method, a shutdownHook thread is first registered to implement the SpringBean destruction life cycle callback.

In the console after executing the refresh, you can see that the beans of tomcat and some IOC containers have been loaded.

(8) Spring application context closing stage

There is no content processing in afrerRefresh(), and the method will not be changed in subsequent versions.

protected void afterRefresh(ConfigurableApplicationContext context,
 ApplicationArguments args) {
    }

The end timer stops and the listener's started() is called.

(9) Callback work processing

Enter the method analysis

ApplicationContext is the IOC container. This method obtains all ApplicationRunner and ConmmandLineRunner from the IOC container , and then traverses and calls back.

The two classes used in callRunners are almost equivalent. They are both used to do some customer-defined work, and the run method of the user-defined implementation class is called only after the entire process is completed. The two run methods The implementation methods are all called when the container is basically initialized.

Immediately afterwards, if there is no exception, the code is executed as follows:

The listener callback running() method represents the normal start and end of SpringApplication.

(10) SpringApplication startup exception handling

The main reason for exceptions is the handling of exceptions. Let’s go into this method analysis.

You can see that this exception reporting class also supports customization and automatic configuration. After the configuration is completed, Springboot does some basic finishing work and returns the application environment context (IOC container).

4. SpringBoot automatic configuration analysis

Spring Boot's automated configuration module is one of the core features of the framework, which can greatly simplify application configuration work. The following is an explanation and analysis of the Spring Boot automated configuration module:

  1. Principle of automated configuration : Spring Boot's automated configuration module is based on the principle of convention over configuration . It automatically configures the various components of the application by scanning the classpath for dependencies and configurations. It uses a conditional configuration mechanism to automatically select appropriate configurations based on the environment and conditions.
  2. Implementation of automatic configuration : Spring Boot automated configuration module uses @Conditional annotations and conditional annotations to implement conditional configuration. These annotations can determine whether to enable a configuration based on a set of conditions. For example, @ConditionalOnClass determines whether a configuration is enabled based on whether the specified class exists in the classpath.
  3. Loading order of automatic configuration : Spring Boot's automatic configuration is implemented through the automatic configuration class defined in the META-INF/spring.factories file under the classpath. These auto-configuration classes are automatically loaded and initialized and configured based on conditions. Depending on the conditions, multiple auto-configuration classes can be loaded, and they will be configured in order of priority .
  4. Customization of automatic configuration : Spring Boot allows developers to customize automatic configuration. You can use @Conditional annotations and conditional annotations to define custom conditions to affect the behavior of automatic configuration. You can also use the @EnableAutoConfiguration annotation to control enabling or disabling automatic configuration.
  5. Benefits of automatic configuration : Spring Boot’s automated configuration module brings many benefits. It greatly reduces the workload of manual configuration and improves development efficiency. It provides sensible default configurations, reducing the risk of misconfiguration. At the same time, its conditional configuration mechanism makes applications more flexible and can be dynamically configured according to different environments and needs.

Overall, Spring Boot's automated configuration module is one of the important features of the framework. It realizes automatic loading and configuration of various components of the application through the principle of convention over configuration and conditional configuration mechanism. This provides developers with convenience and flexibility and greatly simplifies the application configuration process.

Now back to our initial diagram analysis, the configuration module mainly uses SpringFactoriesLoader, that is, the Spring factory loader . This object provides the loadFactoryNames method, and the input parameters are factoryClass and classLoader, that is, the factory class name and the corresponding Class loader , the method will load the specified file under the search path of the class loader according to the specified classLoader , that is, the spring.factories file . The incoming factory class is the interface. After obtaining the class names of these implementation classes , the loadFactoryNames method returns A collection of class names. After the method caller obtains these collections, it obtains the class objects and constructors of these classes through reflection, and finally generates instances.

(1) Analysis of automatic assembly principles

From the @EnableAutoConfiguration annotation in @SpringBootApplication, you can see that it imports an automatic configuration import selector AutoConfigurationImportSelect

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

Its class diagram is as follows

It can be found that it finally implements ImportSelector (selector) and BeanClassLoaderAware (bean class loader middleware), in which the function of this selector is to import components.

All automatic assembly logic is implemented in the selectImports method in AutoConfigurationImportSelector

Enter the getAutoConfigurationEntry() method

Enter getCandidateConfigurations() to obtain candidate configuration methods. You can see the loadFactoryNames() method of the core SpringFactoriesLoader.

Among them, SpringFactoriesLoader is the loader of Spring Framework factory mechanism, and loadFactoryNames is its corresponding loading method. Enter the core loadFactoryNames method to view

The loading principle here is as follows:

  • Scan META-INF/spring.factories under all jar package paths. Here, the corresponding url path is generated through the class loader.
  • Pack the scanned content into a properties object, traverse the content of this object, and return a map. The key of the map is the full class name of the interface, and the value is a list of all implementation classes of the interface (the elements in the list are deduplicated to prevent repeated loading) , this value information will later be used as the return value of the loadSpringFactories method.
  • Search and return a list of full class names of implementation classes for the specified class name mapping in the map returned in the previous step.

Look at the getSpringFactoriesLoaderFactoryClass method in the getCandidateConfiguration method just now. What is returned is the EnableAutoConfiguration class.

That is to say, we need to get the values ​​corresponding to this class from the properties just now and add them to the container.

Select the spring.factories file under mybatis-spring-boot-autoconfigure and analyze it:

Each xxxAutoConfiguration class is a component in the container and is added to the container to use them for automatic configuration;

Only when entering the container will these automatic configuration classes take effect - perform automatic configuration functions

(2) Conditional automatic assembly

For automatic configuration classes that use @Configuration, their conditional automated assembly takes @condition as the core. The @conditional annotation in the bottom layer of spring will take effect for the configuration in the entire configuration class based on different conditions.

Conditional assembly can be divided into the following categories:

Class conditional annotation

annotation

illustrate

@ConditionalOnClass

Takes effect when the specified class exists

@ConditionalOnMissingClass

Takes effect when the specified class is missing

Bean condition annotation

annotation

illustrate

@ConditionalOnBean

Takes effect when the specified bean exists

@ConditionalOnMissingBean

Takes effect when the specified bean is missing

Attribute condition annotation

annotation

illustrate

@ConditionalOnProperty

Use the value of the property (application.properties) to determine whether it is effective

Web application condition annotation

annotation

illustrate

@ConditionalOnWebApplication

It takes effect when it is a web type

@ConditionalOnNotWebApplication

It takes effect when it is not a web type.

Other conditional notes

@Conditional extended annotation

Function (determine whether the current specified conditions are met)

@ConditionalOnJava

Does the system's Java version meet the requirements?

@ConditionalOnExpression

Satisfies SpEL expression specification

@ConditionalOnSingleCandidate

There is only one specified Bean in the container, or this Bean is the preferred Bean

@ConditionalOnResource

Whether the specified resource file exists in the classpath

@ConditionalOnJndi

The specified item exists in JNDI

Analyzing @ConditionalOnWebApplication

package org.springframework.boot.autoconfigure.condition;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
    ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;

    public static enum Type {
        ANY,
        SERVLET,
        REACTIVE;

        private Type() {
        }
    }
}

Enter the OnWebApplicationCondition class, which has a getMatchOutcome() method, which is to determine whether it meets the current configuration conditions.

This method first determines whether this annotation is used, and then uses isWebApplication to determine whether it is a web application. These match methods determine if the specified conditions are true, and then components will be added to the container and the configuration content will take effect.

(3) Example of automatic configuration principle: HttpEncodingAutoConfiguration (HTTP encoding automatic configuration)

You can see the following information:

  • @Configuration means that this is a configuration class, similar to a written configuration file, and you can also add components to the container.
  • @EnableConfigurationProperties enables the ConfigurationProperties function of the specified class, binds the value of the configuration file application.properties to ServerProperties, and adds ServerProperties to the IOC container.
  • @ConditionalOnClass用来判断当前项目是否含有这个类,这里CharacterEncodingFilter的作用就是springMVC乱码解决的过滤器(以前在spring的xml文件中配置的),如果有这个过滤器则配置生效。
  • @ConditionalOnProperty判断这个配置是否存在,matchIfMissing = true代表即使配置文件中不存在这个属性也是默认生效的。

需要注意的是,在spring.factories中的自动配置类不是都能生效的,都有各自的生效条件。根据当前不同条件判断,来决定这个配置类是否生效;一旦配置类生效,这个配置类就会给容器添加各种组件,这些组件的属性从对应的properties中获取,这些类里面的每一个属性又是和配置文件绑定的。

Guess you like

Origin blog.csdn.net/xiaofeng10330111/article/details/130903779