In this article we’ll do a step-by-step example to see how SpringBoot’s automatic configuration is implemented, and then look at some of the extension points SpringBoot has left us with.

Make your own SpringBoot Starter

We know that SpringBoot provides a large number of initiators, introducing initiator dependencies can directly enjoy automatic dependency configuration and automatic property configuration:

Github.com/spring-proj…

In the first article, I mentioned that before The advent of SpringBoot, we needed to use SpringMVC, Spring Data, and Spring Core to configure beans and Bean dependencies for various components within Spring. 90% of the time, we used the default configuration. No custom extension classes, this time also need to be manually configured by the user is obviously not reasonable, with SpringBoot, we only need to introduce the launcher dependency, and then the launcher can do automatic configuration as some of their own internal components, greatly convenient for the user. The implementation of the launcher is very simple, let’s look at the implementation process. Start by creating an empty Maven project and introducing SpringBoot:

<?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>

    <groupId>me.josephzhu</groupId>
    <artifactId>spring101</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring101</name>
    <description></description>

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

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
Copy the code

Then we create a Starter module attached to the parent project:

<?xml version="1.0" encoding="UTF-8"? >
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>

    <groupId>me.josephzhu</groupId>
    <artifactId>spring101-customstarter</artifactId>
    <version>0.0.1 - the SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring101-customstarter</name>
    <description></description>

    <parent>
        <groupId>me.josephzhu</groupId>
        <artifactId>spring101</artifactId>
        <version>0.0.1 - the SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>

</project>
Copy the code

Next we create an abstract base class and implementation for the service, which is very simple, depends on properties, and then has different implementations (the no-argument constructor sets the default value of the greeting to Hello) :

package me.josephzhu.spring101customstarter;

import org.springframework.beans.factory.annotation.Autowired;

public abstract class AbstractMyService {

    protected String word;
    public AbstractMyService(String word) {
        this.word = word;
    }

    public AbstractMyService(a) {
        this ("Hello");
    }

    @Autowired
    protected MyServiceProperties properties;

    public abstract String hello(a);
}
Copy the code

Here the custom property class is injected:

package me.josephzhu.spring101customstarter;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring101")
@Data
public class MyServiceProperties {
    /** * user name */
    private String name;
    /** * user age *Should between 1 and 120 */
    private Integer age;
    /** * determine the service version you want use */
    private String version;
}
Copy the code

Here you see how easy it is if we need to define a custom class to associate a configuration source (such as an application.properties file configuration), we can annotate our POJO with the @ConfigurationProperties annotation to tell the annotation that we configured the prefix. As an added bonus, if we want our IDE to have hints for custom configurations (done automatically, with hints in annotations), we can introduce the following dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>
Copy the code

Json file called spring-configuration-metadata.json is generated under meta-INF:

{
  "hints": []."groups": [{"sourceType": "me.josephzhu.spring101customstarter.MyServiceProperties"."name": "spring101"."type": "me.josephzhu.spring101customstarter.MyServiceProperties"}]."properties": [{"sourceType": "me.josephzhu.spring101customstarter.MyServiceProperties"."name": "spring101.age"."description": "user age *Should between 1 and 120"."type": "java.lang.Integer"
    },
    {
      "sourceType": "me.josephzhu.spring101customstarter.MyServiceProperties"."name": "spring101.name"."description": "user name"."type": "java.lang.String"
    },
    {
      "sourceType": "me.josephzhu.spring101customstarter.MyServiceProperties"."name": "spring101.version"."description": "determine the service version you want use"."type": "java.lang.String"}}]Copy the code

You can then use the configuration when prompted:

Let’s write the first service implementation as follows, just to print out some of the custom properties used:

package me.josephzhu.spring101customstarter;

import org.springframework.stereotype.Service;

@Service
public class MyService extends AbstractMyService {

    public MyService(String word) {
        super(word);
    }

    public MyService(a){}

    @Override
    public String hello(a) {
        return String.format("V1 %s >> %s:%s !!", word, properties.getName(), properties.getAge()); }}Copy the code

The next step is to define the auto-configuration class:

package me.josephzhu.spring101customstarter;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyAutoConfiguration {
    @Bean
    MyService getMyService() {returnnew MyService(); }}Copy the code

Told by EnableConfigurationProperties Spring we need a Configuration file associated Configuration class (Configuration class bucket does not need to set up @ Component), told the Spring by @ the Configuration which is a Bean Configuration class, Below we define the implementation of our Service. Finally, we need to tell SpringBoot how to find our auto-configuration classes and automatically configure them when appropriate. We need to create a meta-INF folder in the project resources directory and create a Spring. factories file with the following content:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=me.josephzhu.spring101customstarter.MyAutoConfiguration
Copy the code

Ok, that’s it, let’s create a project to try using our custom launcher:

<? 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 > < groupId > me. Josephzhu < / groupId > < artifactId > spring101 - the main < / artifactId > < version > 0.0.1 - the SNAPSHOT < / version > < packaging > jar < / packaging > < name > spring101 - the main < / name > < description > < description > Josephzhu </groupId> <artifactId>spring101</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>me.josephzhu</groupId> <artifactId>spring101-customstarter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>Copy the code

Create a Runner to invoke the service:

package me.josephzhu.spring101main;

import lombok.extern.slf4j.Slf4j;
import me.josephzhu.spring101customstarter.AbstractMyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class Runner1 implements CommandLineRunner {

    @Autowired
    private AbstractMyService service;

    @Override
    public void run(String... args) { log.info(service.hello()); }}Copy the code

Create main program:

package me.josephzhu.spring101main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Spring101MainApplication {

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

Then create the application.properties file in the resources directory of the main module and write two configurations:

spring101.age=35
spring101.name=zhuye
Copy the code

After running, you can see the output:

The 2018-09-30 14:55:00. 12704-848 the INFO [main] me. Josephzhu. Spring101main. Runner1: V1 Hello > > zhuye: 35!!!!!Copy the code

It can be proved that the starter introduced by our main module is correctly recognized and loaded by SpringBoot, and the Configuration in the second starter is correctly executed, which not only loads the Configuration class, but also correctly injects an implementation of Service.

How to implement conditional configuration?

As component developers, we need to use Spring’s Conditional features when we want to automate various intelligent configurations for environment, configuration, class loading, and so on. As an example, if our Service evolves to v2, we want the user to use v1 by default, with the version attribute configured to allow the user to switch to V2 if necessary. It is very simple to implement, first define another v2 version of the service:

package me.josephzhu.spring101customstarter;

import org.springframework.stereotype.Service;

@Service
public class MyServiceV2 extends AbstractMyService {

    public MyServiceV2(String word) {
        super(word);
    }

    public MyServiceV2(a){}

    @Override
    public String hello(a) {
        return String.format("V2 %s >> %s:%s !!", word, properties.getName(), properties.getAge()); }}Copy the code

There is no difference from version V1, except that the v2 keyword is marked. Then let’s modify our auto-configuration class:

package me.josephzhu.spring101customstarter;

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(MyServiceProperties.class)
public class MyAutoConfiguration {
    @Bean
    @ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v1", matchIfMissing = true)
    MyService getMyService(a){
        return new MyService();
    }

    @Bean
    @ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v2")
    MyServiceV2 getMyServiceV2(a){
        return newMyServiceV2(); }}Copy the code

The @conditionalonproperty annotation has been added to the beans, which is self-explanatory. Matches the default v1 version of the service if the version value is v1 or version is not defined, matches the v2 version of the service if the configuration is set to v2, and that’s it. For another example, if our consumer wants to define the implementation of the service, we need to override v1 and v2, which are automatically configured for our autoloassembly, using another annotation @conditionalonmissingBean to tell SpringBoot, Automatic configuration if the Bean is not found:

package me.josephzhu.spring101customstarter;

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(MyServiceProperties.class)
public class MyAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(MyService.class)
    @ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v1", matchIfMissing = true)
    MyService getMyService(a){
        return new MyService();
    }

    @Bean
    @ConditionalOnMissingBean(MyServiceV2.class)
    @ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v2")
    MyServiceV2 getMyServiceV2(a){
        return newMyServiceV2(); }}Copy the code

This way, if the client defines its own implementation of the Service, it can make automatic configuration go away and use client-defined beans. There are also countless other Conditional annotations that can be used, and you can even customize conditions by looking at official documentation.

Let’s test it out

Next, let’s write unit tests to verify our previous code. Using ApplicationContextRunner makes it easy to set the various external configuration items and custom configuration classes brought in: Here we write three test cases:

  • After the appropriate property configuration is provided, you can see that the output of the service gets the property correctly.
  • Use the version configuration item to switch the implementation of the service. Omit version, set version to 1, and set version to 2 to get the correct output, which is v1, v1, and v2 respectively.
  • After the client custom implementation (MyServiceConfig), you can see that the service implementation defined in the automatic configuration is not loaded, and finally output the greeting Hi instead of Hello.
package me.josephzhu.spring101main;

import me.josephzhu.spring101customstarter.AbstractMyService;
import me.josephzhu.spring101customstarter.MyAutoConfiguration;
import me.josephzhu.spring101customstarter.MyService;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import static org.assertj.core.api.Assertions.assertThat;


public class Spring101MainApplicationTests {

    private ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(MyAutoConfiguration.class));

    @Test
    public void testService(a) {
        applicationContextRunner
                .withPropertyValues("spring101.age=35")
                .withPropertyValues("spring101.name=zhuye")
                .run(context -> {
                    assertThat(context).hasSingleBean(AbstractMyService.class);
                    assertThat(context.getBean(AbstractMyService.class).hello()).containsSequence("zhuye:35");
                    System.out.println(context.getBean(MyService.class).hello());
                });
    }

    @Test
    public void testConditionalOnProperty(a) {
        applicationContextRunner
                .run(context -> {
                    assertThat(context).hasSingleBean(AbstractMyService.class);
                    assertThat(context.getBean(AbstractMyService.class).hello()).containsSequence("V1 Hello");
                    System.out.println(context.getBean(AbstractMyService.class).hello());
                });
        applicationContextRunner
                .withPropertyValues("spring101.version=v1")
                .run(context -> {
                    assertThat(context).hasSingleBean(AbstractMyService.class);
                    assertThat(context.getBean(AbstractMyService.class).hello()).containsSequence("V1 Hello");
                    System.out.println(context.getBean(AbstractMyService.class).hello());
                });
        applicationContextRunner
                .withPropertyValues("spring101.version=v2")
                .run(context -> {
                    assertThat(context).hasSingleBean(AbstractMyService.class);
                    assertThat(context.getBean(AbstractMyService.class).hello()).containsSequence("V2 Hello");
                    System.out.println(context.getBean(AbstractMyService.class).hello());
                });

    }

    @Test
    public void testConditionalOnMissingBean(a) {
        applicationContextRunner
                .withUserConfiguration(MyServiceConfig.class)
                .run(context -> {
                    assertThat(context).hasSingleBean(MyService.class);
                    assertThat(context.getBean(MyService.class).hello()).containsSequence("V1 Hi"); System.out.println(context.getBean(MyService.class).hello()); }); }}@Configuration
class MyServiceConfig {
    @Bean
    MyService getService(a) {
        return new MyService("Hi"); }}Copy the code

When you run the test, you can see that all three tests pass, and the console outputs the return value of the Hello method:

Implement a custom configuration data source

Next, let’s look at how to implement a custom configuration data source using EnvironmentPostProcessor. We create a new class in the Starter project that uses a Yaml configuration source loader, and we add the loaded custom PropertySource to the first of the PropertySource candidate lists. This allows the attributes to be read first from config.yml (classpath) we defined:

package me.josephzhu.spring101customstarter;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

public class MyPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { PropertySource<? > propertySource = loadYaml(new ClassPathResource("config.yml"));
        environment.getPropertySources().addFirst(propertySource);
    }

    privatePropertySource<? > loadYaml(Resource path) {if(! path.exists()) {throw new IllegalArgumentException("Resource " + path + " does not exist");
        }
        try {
            return this.loader.load(path.getFile().getAbsolutePath(), path).get(0);
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to load yaml configuration from "+ path, ex); }}}Copy the code

The most important step is to get SpringBoot to load into our PostProcessor. Again, add a configuration to the Spring, Factories file:

org.springframework.boot.env.EnvironmentPostProcessor=me.josephzhu.spring101customstarter.MyPropertySourceEnvironmentPos tProcessorCopy the code

Now we can verify this by creating a config.yml in the resrouces directory under the Starter project:

spring101:
  name: zhuye_yml
Copy the code

Rerun the main project and you can see the following output containing the word YML:

The 2018-09-30 15:27:05. 12769-123 the INFO [main] me. Josephzhu. Spring101main. Runner1: V1 Hello > > zhuye_yml: 35!!!!!Copy the code

We can add the Actuator module for further verification:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Copy the code

Add Settings to the configuration file to allow all port access:

management.endpoints.web.exposure.include=*
Copy the code

Then open the browser to http://127.0.0.1:8080/actuator/env:

Debugging problems with automatic configuration

For complex projects, if we find that automatic configuration does not work, it is quite troublesome to figure out how the framework does automatic configuration in various conditions and the matching process of automatic configuration. At this time, we can open SpringBoot Debug to check the log:

MyAutoConfiguration#getMyService matched:
      - @ConditionalOnProperty (spring101.version=v1) matched (OnPropertyCondition)
      - @ConditionalOnMissingBean (types: me.josephzhu.spring101customstarter.MyService; SearchStrategy: all) did not find any beans (OnBeanCondition)
MyAutoConfiguration#getMyServiceV2:
      Did not match:
         - @ConditionalOnProperty (spring101.version=v2) did not find property 'version' (OnPropertyCondition)
Copy the code

We can try to set version to 3 in the configuration file by using the prompt given by the system when an error occurs:

spring101.version=v3
Copy the code

Run again and see the following output:

***************************
APPLICATION FAILED TO START
**************************
Description:
Field service in me.josephzhu.spring101main.Runner1 required a bean of type 'me.josephzhu.spring101customstarter.AbstractMyService' that could not be found.
	- Bean method 'getMyService' in 'MyAutoConfiguration' not loaded because @ConditionalOnProperty (spring101.version=v1) found different value in property 'version'
	- Bean method 'getMyServiceV2' in 'MyAutoConfiguration' not loaded because @ConditionalOnProperty (spring101.version=v2) found different value in property 'version'
Action:
Consider revisiting the entries above or defining a bean of type 'me.josephzhu.spring101customstarter.AbstractMyService' in your configuration.
Copy the code

This so-called analysis report is quite mechanical, and as framework developers we can even customize something called FailureAnalyzer to make more explicit suggestions. The implementation is almost the same as the previous steps, starting with a custom class:

package me.josephzhu.spring101customstarter;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;

public class MyFailureAnalyzer extends AbstractFailureAnalyzer<NoSuchBeanDefinitionException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) {
        if(cause.getBeanType().equals(AbstractMyService.class))
            return new FailureAnalysis("Failed to load MyService"."Please check if the version property setting in the configuration file is v1 or v2", rootFailure);

        return null; }}Copy the code

If the error occurred because of our Service type, tell the user why the error occurred (Description) and how to correct the error (Action). Then do the same with spring.factories:

org.springframework.boot.diagnostics.FailureAnalyzer=me.josephzhu.spring101customstarter.MyFailureAnalyzer
Copy the code

Rerun the program and you should see the following:

* * * * * * * * * * * * * * * * * * * * * * * * * * * the APPLICATION FAILED TO START * * * * * * * * * * * * * * * * * * * * * * * * * * * Description: loading MyService failure Action: Check whether the version attribute in the configuration file is v1 or v2Copy the code

Is that a lot more intuitive? Here my implementation is relatively simple, in the formal implementation you can according to the context and various circumstances of the environment for users to carry out a comprehensive analysis, analysis of the cause of service startup failure.

SpringBoot extension point

In the previous examples, we did various extension configurations, including automatic configuration, post-environment processor configuration, and error parser configuration using Spring.Factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=me.josephzhu.spring101customstarter.MyAutoConfiguration org.springframework.boot.env.EnvironmentPostProcessor=me.josephzhu.spring101customstarter.MyPropertySourceEnvironmentPos tProcessor org.springframework.boot.diagnostics.FailureAnalyzer=me.josephzhu.spring101customstarter.MyFailureAnalyzerCopy the code

SpringBoot also has several other expansion slots. Here are some configuration classes that come with SpringBoot:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesEnvironmentPostProcessor

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider
Copy the code

We can see that there are eight broad categories of extension slots throughout the entire SpringBoot loading process, which interested readers can search through the Spring documentation and source code for further analysis. This article starts with how to do a Starter to implement automatic configuration, further explains how to implement intelligent conditional configuration, how, how to perform automatic configuration testing, and then we customize the environment post processor to load additional configuration sources (you can of course implement more complex configuration sources, For example, retrieving configurations from Redis and databases) and by enabling Actutor for verification, the Configuration error analyzer is defined to give the user explicit error alerts. Finally, we looked in Spring.Factories to see some of the extension slots and implementations defined internally by SpringBoot.

The code for this article is at github.com/JosephZhu19…