Operation principle of SpringBoot

Posted by DEVILofDARKNESS on Sat, 18 Sep 2021 06:07:19 +0200

Operation principle of SpringBoot

Since I wrote the first SpringBoot program, I have touched the fish for a few days. Now I come back to study the operation principle of SpringBoot!

1. Dependent file pom

Previously, SpringBoot created a Maven project, so the corresponding configuration file pom.xml must contain all the dependencies required by the project. Click pom.xml to see that there are only a few initiator dependencies, but you can see that there is a parent project spring boot starter parent for this project

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

Click the parent project to view it. It is found that there is no dependencies part, only build configurations such as file export and plug-ins; But it relies on a parent project, spring boot dependencies

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.5.4</version>
  </parent>

Click the parent project again and see more than 2000 lines of dependency configuration, which is where the jar package dependency required by the project lies! Among them, many dependencies are configured with corresponding versions, such as

	<aspectj.version>1.9.7</aspectj.version>

When referencing, directly reference the version number of the configuration

      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
      </dependency>

Therefore, when we introduce some dependencies, we do not need to specify the version number, because it has been selected in the dependency configuration! However, when introducing a dependency that is not in the dependency configuration, you still need to specify its version number.

2. Starter

Returning to the original pom file, you can see the dependencies of the SpringBoot launcher

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

The function of the launcher is to load the running environment of the module. For example, spring boot starter above loads the running environment of SpringBoot, and spring boot starter Web loads the running environment of the Web module!

SpringBoot extracts the running environment of modules as initiators. For what modules need to be run, just introduce the corresponding initiators to introduce all the required dependencies!

3. Main startup class annotation

After creating the SpringBoot project, you can see that the project comes with a class with main method, namely the main startup class

@SpringBootApplication
public class SpringBoot01HelloApplication {

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

}

First, analyze what the main startup class is from the annotation!

3.1 @SpringBootApplication

@SpringBoot application: the label on the class indicates that this class is the main startup class of SpringBoot, and SpringBoot will run the main method of this class to start the SpringBoot application.

Click this annotation and you can see many other annotations, including three important ones

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication

Replace the @ SpringBootApplication annotation on the main startup class with these three annotations, and the program can still run; However, for convenience, just use @ SpringBootApplication directly!

Among them, @ ComponentScan has been seen in Spring. It is a very important annotation to automatically scan and load qualified components (beans).

3.2 @SpringBootConfiguration

@SpringBoot configuration: the annotation on the class indicates that this class is the configuration class of SpringBoot.

Click this annotation to see the @ Configuration annotation

@Configuration
public @interface SpringBootConfiguration

Note the class marked with this annotation is a configuration class, corresponding to the XML configuration file in Spring!

Continue to drill down and see the @ Component annotation again

@Component
public @interface Configuration

This shows that the main startup class is also a component in Spring. It is responsible for starting the application!

3.3 @EnableAutoConfiguration

@EnableAutoConfiguration: marked on the class indicates that the automatic Configuration function is enabled. All qualified @ Configuration configurations are created as bean s and loaded into the IoC container of the current SpringBoot.

Click into this annotation and you can see

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration

Click the @ AutoConfigurationPackage annotation to see the @ import annotation

@Import({Registrar.class})
public @interface AutoConfigurationPackage

That is, the function of @ AutoConfigurationPackage annotation is realized by @ Import annotation. Its main function is to scan the components under the package and all sub packages of the main startup class into the IoC container of Spring, which is why the new package should be at the same level as the main startup class!

Another annotation @ Import(AutoConfigurationImportSelector.class) is the most critical. Through the AutoConfigurationImportSelector class, @ EnableAutoConfiguration allows the SpringBoot application to create all qualified @ Configuration configurations as bean s and load them into the IoC container of the current SpringBoot.

In the AutoConfigurationImportSelector class, you can see the method getCandidateConfigurations to get candidate configurations

    // Obtain candidate configurations
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // The annotation class EnableAutoConfiguration for starting the automatic import configuration file you saw at the beginning is returned here
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

This method calls the loadFactoryNames method of the springfactoryesloader class to get the configuration class

    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ...
        String factoryTypeName = factoryType.getName();
        // The loadSpringFactories method is called again
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

The loadSpringFactories method under the same class is called again. This method is a little long, but it mainly does two things

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        // Get the resource named "META-INF/spring.factories"
        // ...
        // Traverse the read resources and encapsulate them into a property
        // ...
    }

I'm dizzy now, but I can see a name META-INF/spring.factories that appears many times. Search this file and find it in spring-boot-autoconfigure-2.5.4.jar. It seems to have a lot to do with automatic assembly! Look what it has

It's estimated that it's a bit of a sudden. spring.factories contains all the configuration classes that SpringBoot needs to assemble automatically. Only by automatically reading and assembling them can we realize the effect that SpringBoot can run directly without any configuration!

For example, find a familiar configuration about WebMVC in it

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\

Click this configuration class to see the familiar configuration of WebMVC such as Prefix suffix and static resource filtering, that is, the configuration when using spring MVC before has been completed (I'm dizzy, maybe so).

Summary

  1. When SpringBoot starts, obtain the configuration classes to be automatically assembled from the META-INF/spring.factories file under the classpath. Import these configuration classes as bean s into the IoC container, and the automatic configuration will take effect.
  2. Not all AutoConfiguration autoconfiguration classes will be assembled. It is also necessary to judge whether they meet the assembly conditions @ ConditionalOn... (annotation of AutoConfiguration class). Only those classes that meet the conditions will be assembled!
  3. The AutoConfiguration auto configuration class imported in the container is all the components required for the current running scenario, and has been configured, which saves us the work of manual configuration!

4. Main startup method

There is only one main method and one sentence in the main startup class

@SpringBootApplication
public class SpringBoot01HelloApplication {

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

}

Run the main method of the main startup class to start the whole SpringBoot service! The core is the SpringApplication.run method, which consists of two parts

  1. Instantiation of SpringApplication object
  2. Execution of run method

Looking at the constructor of the SpringApplication class, you can see that it mainly does four things

  1. Determine whether the application type is a normal project or a Web project

    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
  2. Load all available initializers and set them to the initializers property (list < applicationcontextinitializer <? > > initializers)

    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
  3. Load all available program listeners and set them to the listeners property (list < applicationlister <? > > listeners)

    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    
  4. Infer and set the definition class of the main method, and find the main class to run

    this.mainApplicationClass = this.deduceMainApplicationClass();
    

As for the execution process of the run method, I'll talk about it later. I'm going to vomit.

5. Summary

Simply understand the operation principle of SpringBoot... It can only be said that it is hard to see, and there is no way if you can't understand it.

A little understanding: SpringBoot judges which Configuration classes to be automatically assembled through the dependence of the initiator. These Spring Configuration classes adopt the JavaConfig method, that is, they are configured with the @ Configuration annotation, and many default configurations are made, that is, the so-called conventions; If the Configuration is not changed manually, it will run according to the default Configuration, avoiding the repeated Configuration process, that is, the Convention is greater than the Configuration.

I hope a little understanding is correct. This kind of thing should be put to the end 😡!

Topics: Spring Spring Boot