Automatic configuration principle of springboot

During the interview, there is no need to answer so specific, you just need to answer like this:

When Spring Boot starts, it will find all the automatic configuration classes in the META-INF/spring.factories configuration file through the @EnableAutoConfiguration annotation, and load them, and these automatic configuration classes are named after AutoConfiguration, which is actually It is a Spring container configuration class in the form of JavaConfig, which can obtain the properties configured in the global configuration file through the class named at the end of Properties, such as server.port, and the XxxxProperties class corresponds to the global configuration file through the @ConfigurationProperties annotation Properties are bound.

I. Introduction

I believe friends who have been in contact with springboot know that springboot has various Starters to facilitate the introduction of dependencies, and at the same time, you can directly check and add them through the IDE. It is very convenient. The core of SpringBoot is automatic configuration, and the ones that support automatic configuration are Starter projects. In addition to the official starter, users can also customize their own Starter projects according to the rules.

The personal understanding of the relationship between Starter and automatic configuration is this: After adding a Starter to the project, if there is no special need, basically it can be used directly without any configuration. The reason is that the Starter has been loaded through [Auto Configuration] Some default configuration; if necessary, you can set some configuration items to override the default configuration through the springboot configuration file (such as application.yml).

Two, custom Starter

1. Description

I personally think that before understanding the principle of [Automatic Configuration], it is best to understand the process of customizing the starter, so that it is easier to understand the automatic configuration.

Custom starter scenario: If the entry class that comes with springboot cannot meet the requirements, you can customize Starter

2. Use

1) Create a project

First, we must name the starter:

  • Spring official Starter: named [spring-boot-starter-name], such as: spring-boot-starter-web
  • Spring officially recommends an unofficial custom Starter: name it [name-spring-boot-starter], such as myxxx-spring-boot-starter

Use IDEA to create a normal maven project

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

Note 1: Among them, spring-boot-starter-web will quote spring-boot-starter and spring-boot-autoconfigure will be quoted in spring-boot-starter (There are many examples on the Internet that directly quote one of the latter two, which is also possible)

Note 2: In order to use dependencyManagement for dependency version management, you can set the parent project of the custom starter project as spring-boot-starter-parent

Note 3: The custom Starter cannot have a start entry, which means it can only be used as a tool project. Therefore, do not write the pom.xml of the custom Starter as a startable project

The complete pom.xml is as follows:

<?xml version="1.0" encoding="UTF-8"?>
 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.15.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
 
  <groupId>com.mzj.myproject</groupId>
  <artifactId>myproject-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>
 
  <name>myproject-spring-boot-starter</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
 
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
</project>

3) Custom properties

Note: When using the official Spring Starter, you can configure parameters in the application.yml or application.properties file to override the default value. When customizing Starter, you can also configure the properties class as needed to save configuration information. The configuration prefix here uses spring.mystarter uniformly

package com.mzj.myproject;
import org.springframework.boot.context.properties.ConfigurationProperties;
 
@ConfigurationProperties(prefix = "spring.mystarter")
public class MyStarterProperties {
    // 参数
    private String parameter;
    private String url;
    private int port;
 
    public String getParameter() {
        return parameter;
    }
 
    public void setParameter(String parameter) {
        this.parameter = parameter;
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
 
    public int getPort() {
        return port;
    }
 
    public void setPort(int port) {
        this.port = port;
    }
}

4) Define core service classes

Note: Each starter has its own business functions to be processed, so define a service class. This service class has two functions, one is the functional service of the introduced project itself, and the other is used to judge the springboot automatic configuration.

package com.mzj.myproject;
 
public class MyStarterService {
    private MyStarterProperties myproperties;
 
    public MyStarterService() {
    }
 
    public MyStarterService(MyStarterProperties myproperties) {
        this.myproperties = myproperties;
    }
 
    public String print() {
        System.out.println("参数1: " + myproperties.getParameter());
        System.out.println("参数2: " + myproperties.getUrl());
        System.out.println("参数3: " + myproperties.getPort());
        String s = myproperties.getParameter();
        return s;
    }
}

5) Define the automatic configuration class

Each Starter generally has at least one automatic configuration class, and the naming rule is: [name+AutoConfiguration], such as: MyStarterServiceAutoConfiguration.

@Configuration is used to declare this class as a configuration class;

The @ConditionalOnClass annotation indicates that automatic configuration will only be performed when the MyStarterService class exists in the classpath;

@EnableConfigurationProperties makes the class annotated with @ConfigurationProperties take effect, here is MyStarterProperties, that is, the corresponding property configuration in application.properties is set in the MyStarterProperties object;

The annotations on the myStarterService method,

  • @Bean indicates that the object instantiated by this method will be loaded into the container;
  • @ConditionalOnMissingBean automatically configures the MyStarterService class when no Bean is specified in the container
  • @ConditionalOnProperty specifies that the corresponding automatic assembly will be performed only when spring.mystarter.enabled=true in the configuration file.

The configuration method is shown in the following code:


package com.mzj.myproject;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@EnableConfigurationProperties(MyStarterProperties.class)//@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效,这里是MyStarterProperties
/**
 * Description: 当类路径classpath下有指定的类的情况下进行自动配置
 */
@ConditionalOnClass(MyStarterService.class)
/**
 * Description: 配置文件中matchIfMissing =true时进行自动配置
 * 
 * 配置文件中spring.mystarter.enabled=true时才进行相应的自动装配
 */
@ConditionalOnProperty(prefix = "spring.mystarter", value = "enabled", matchIfMissing = true)
public class MyStarterServiceAutoConfiguration {
    @Autowired
    private MyStarterProperties myproperties;//使用配置
 
    @Bean
    /**
     * Description: 当容器中没有指定Bean的情况下,自动配置MyStarterService类
     */
    @ConditionalOnMissingBean(MyStarterService.class)
    public MyStarterService myStarterService() {
        MyStarterService myStarterService = new MyStarterService(myproperties);
        return myStarterService;
    }
}

The creation of MyStarterService is created in the myStarterService method in the Configuration class (MyStarterServiceAutoConfiguration)

Finally, when all the basic code and automatic configuration classes are ready, they need to be registered: create a new directory META-INF under the resources file, create a new spring.factories file in the directory, and configure the automatic configuration class XXXXAutoConfiguration in the file (MyStarterServiceAutoConfiguration):

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mzj.myproject.MyStarterServiceAutoConfiguration

Register the MyStarterServiceAutoConfiguration class in the spring.factories configuration file. If there are multiple auto-configuration classes, just separate the lines with commas.

6) Package release

At this point, an automatic configuration starter based on Spring Boot has been completed.

After completing the above configuration, you can package and generate the JAR file, and then you can use it like the official Starter. Use "maven:install" to package it to a local maven warehouse or upload it to a private server. Other projects can be used through maven dependencies.

If you do not publish to the Maven repository, you need to manually add dependencies:

7) Test

When creating a new ordinary springboot project, IDEA only chose to rely on spring-boot-starter-web when creating the project

Then add our custom starter dependency in pom.xml:

<dependency>
	<groupId>com.mzj.myproject</groupId>
	<artifactId>myproject-spring-boot-starter</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

Test one:

Set the following parameters in the project application.yml:

Then you can inject the service dependency in the starter package where you need to use it, such as the following Controller:


package com.mzj.myproject.controller;
 
import com.mzj.myproject.MyStarterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
@RequestMapping("teststarter")
public class TestController {
 
    @Autowired
    private MyStarterService myStarterService;
 
    @RequestMapping(value = "get", method = RequestMethod.GET)
    public String teststarter(String userName) {
        System.out.println("myStarterService :" + myStarterService.print());
        return userName;
    }
}

Then start the springboot project, postman input: http://localhost:8080/teststarter/get?userName=123

Console output:

Test 2: Put spring.mystarter.enabled=false in the configuration file, then automatic assembly is not performed, and an exception not found when accessing the service:

If you do not set the spring.mystarter.enabled property, the default is true

8) Conclusion

It can be seen from this example that the MyStarterService object is automatically configured and the test passes.

For our customized starter, we can further expand it to achieve various basic functions. When other projects need it, we only need to [introduce the corresponding dependency] + [configure specific parameters] to use it immediately. Is it very convenient?

9) Summary

The regular Starter is an independent project that can be registered and released in the Maven warehouse for other developers to use. The custom Starter includes the following aspects:

  • Automatic configuration file: Determine whether to perform automatic configuration of the function according to whether there is a specified class under the classpath (automatic configuration only if it exists-@ConditionalOnClass(MyStarterService.class) annotation of the automatic configuration class)
  • The @ConditionalOnProperty annotation can be used to set whether the automatic assembly is started or not can be set by parameters
  • spring.factories: guide springboot to find the specified automatic configuration file
  • Endpoint: contains the description, interface, and interaction of the service (query for business information)-not covered in this article
  • health indicator: the health indicator of the service provided by the Starter-not covered in this article

Three, springboot automatic configuration principle

Spring Boot's source code for automatic configuration is in spring-boot-autoconfigure-xxxxjar.

Having mastered the custom starter, let's take a look at the principle of spring automatic configuration:

1. The entry class of springboot program should be declared with @SpringBootApplication annotation, which is the core annotation of SpringBoot

package com.mzj.myproject;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2. In the @SpringBootApplication annotation, the most important thing is the @EnableAutoConfiguration annotation. With such a straightforward name, you can see that it needs to enable automatic configuration, and then enter the @EnableAutoConfiguration annotation.

3. The @EnableAutoConfiguration annotation is also a derived annotation. You can see that the @import annotation is used in the @EnableAutoConfiguration annotation to complete the function of importing configuration:

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.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.SpringFactoriesLoader;
 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
 
    //略
}

The @Import annotated parameter AutoConfigurationImportSelector uses the SpringFactoriesLoader.loadFactoryNames method to scan the jar package with the META-INF/spring.factories file. The following is the source code of 2.1.16.RELEASE:

This @EnableAutoConfiguration annotation is indirectly marked on the Spring Boot startup class through @SpringBootApplication. The selectImports() method will be executed inside SpringApplication.run(...), find the class corresponding to the fully qualified name of all JavaConfig auto-configuration classes, and then load all auto-configuration classes into the Spring container.

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
				annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = filter(configurations, autoConfigurationMetadata);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				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;
	}

Any springboot application will introduce spring-boot-autoconfigure (because the springboot entry class needs to be modified by @SpringBootApplication annotation, and this annotation is in the spring-boot-autoconfigure package, the specific method is through direct introduction or through introduction such as spring-boot- Starter-web is introduced indirectly), and the spring.factories file is under the package. The spring.factories file is in the form of Key=Value, which is used when multiple Values ​​are separated. The file defines:

  • Initialization information
  • Listener information (application listener, automatic configuration import listener)
  • Filter information
  • The key of the class range that needs springboot for automatic configuration: org.springframework.boot.autoconfigure.EnableAutoConfiguration, as shown below:

The EnableAutoConfiguration above configures multiple classes. These are all built-in related classes that need to be automatically configured in Spring Boot; the corresponding class configuration information will be parsed during the startup process. Each XXXConfiguation class defines the instantiation configuration of related beans. All explain which beans can be automatically configured and under what conditions, and instantiate these beans.

If we customize a starter, we also need to provide the spring.factories file in the starter's jar package and configure it with the configuration class corresponding to org.springframework.boot.autoconfigure.EnableAutoConfiguration. The automatic configuration process of all frameworks is basically the same:

  • Determine whether to introduce the framework (use various condition annotations to limit the effective conditions of automatic configuration)
  • Get configuration parameters
  • Initialize the corresponding components of the framework according to the configuration parameters

Each XxxxAutoConfiguration automatic configuration class takes effect only under certain conditions. The limitations of these conditions are reflected in the form of annotations in Spring Boot. The following are SpringBoot built-in condition annotations (the red ones are frequently used):

  • @ConditionalOnBean : When the specified Bean conditions exist in the SpringIoc container
  • @ConditionalOnClass: When the condition of the specified Class exists in the SpringIoc container
  • @ConditionalOnExpression: Based on SpEL expression as the judgment condition
  • @ConditionalOnJava: Based on the JVM version as the judgment condition
  • @ConditionalOnJndi: Find the specified location when JNDI exists
  • @ConditionalOnMissingBean : When the specified Bean does not exist in the SpringIoc container
  • @ConditionalOnMissingClass: When the condition of the specified Class does not exist in the SpringIoc container
  • @ConditionalOnNotWebApplication: The condition that the current project is not a Web project
  • @ConditionalOnProperty : Whether the specified property has a specified value, such as @ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true), which represents the boolean value of the condition when xxx.xxx is enable. It is also true if it is not set.
  • @ConditionalOnResource: Whether the classpath has a specified value
  • @ConditionalOnSingleCandidate: When the specified Bean is only one in the SpringIoc container, or although there are multiple, specify the preferred Bean
  • @ConditionalOnWebApplication: The current project is a condition of a Web project

The above annotations are all evolved from the meta-annotation @Conditional, and the above specific condition annotations are created according to different conditions.

Example explanation

Reference for this section: https://blog.csdn.net/u014745069/article/details/83820511

Next, take the ServletWebServerFactoryAutoConfiguration configuration class as an example to explain how the attributes in the global configuration file take effect, for example: server.port=8081, how to take effect (Of course, there will be default values ​​if there is no configuration, this default value comes from org. apache.catalina.startup.Tomcat).

The second red box in the above figure is used to load our customized configuration (for example: server.port=9090), and the red box below loads the default configuration (using the tomcat configuration as the default configuration). This way of setting the default configuration is applicable Some of the multiple configurations need to be customized, and some use the default configuration, such as server.ip for customization, while server.port uses the default

On the ServletWebServerFactoryAutoConfiguration class, there is an @EnableConfigurationProperties annotation: open configuration properties , and the parameter behind it is a ServerProperties class. This is where the habit is better than the configuration.

On this class, we see a very familiar annotation: @ConfigurationProperties , its role is to bind properties from the configuration file to the corresponding bean, and @EnableConfigurationProperties is responsible for importing the bean that has been bound to the spring container Medium (see screenshot above). Then all other properties related to this class can be defined in the global configuration file. That is to say, the class that really "limits" which properties we can configure in the global configuration file is these XxxxProperties classes, which are defined in the configuration file The set of attributes at the beginning of the prefix keyword is uniquely corresponding.

At this point, we can roughly understand. Globally configured properties such as server.port, etc., are bound to the corresponding XxxxProperties configuration entity class through the @ConfigurationProperties annotation, encapsulated as a bean, and then imported into the Spring container through the @EnableConfigurationProperties annotation.

And many XxxxAutoConfiguration auto-configuration classes are the JavaConfig form of the Spring container, and their role is to import beans for the Spring container, and the properties required by all imported beans are obtained through the xxxxProperties bean.

During the interview, there is no need to answer so specific, you just need to answer like this:

When Spring Boot starts, it will find all the automatic configuration classes in the META-INF/spring.factories configuration file through the @EnableAutoConfiguration annotation, and load them, and these automatic configuration classes are named after AutoConfiguration, which is actually It is a Spring container configuration class in the form of JavaConfig, which can obtain the properties configured in the global configuration file through the class named at the end of Properties, such as server.port, and the XxxxProperties class corresponds to the global configuration file through the @ConfigurationProperties annotation Properties are bound.

Use an icon to understand this complicated process:

Springboot automatic configuration default value setting and automatic configuration override default value (20201012)

        
1、ServletWebServerFactoryConfiguration:

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {             return new TomcatServletWebServerFactory();         }         ------Create the default attribute in TomcatServletWebServerFactory as 8080


2. WebServerFactoryCustomizerBeanPostProcessor:
    
        execution process

3、ServletWebServerFactoryAutoConfiguration

        Load the Configuration class in the spring.factories file, and create the Service class (created by Properties, here is the Service object created by the automatic configuration 8081 injection) return new TomcatServletWebServerFactoryCustomizer(serverProperties);
        
        here the Service class is TomcatServletWebServerFactoryCustomizer

4. The method of executing Service, here is the customize method of ServletWebServerFactoryCustomizer : override the default value with the personalized value

to sum up

In summary, it is an explanation of the principle of automatic configuration. Of course, when browsing the source code, you must remember not to be too rigid with the implementation of the code, but to grasp the key context:

  • Be sure to remember that the meaning of the XxxxProperties class is: encapsulate the relevant properties in the configuration file;
  • The meaning of XxxxAutoConfiguration class is: automatic configuration class, the purpose is to add components to the container.
  • The other main methods are started to load these various XxxxAutoConfiguration classes.

appendix

1. Springboot supports the parameters configured in application.yml

https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/My_Way666/article/details/112968137