朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神祕

文本我們會一步一步做一個例子來看看 SpringBoot 的自動配置是如何實現的,然後來看一

些 SpringBoot 留給我們的擴展點。

自己製作一個 SpringBoot Starter

我們知道 SpringBoot 提供了非常多的啟動器,引入了啟動器依賴即可直接享受到自動依賴

配置和自動屬性配置:

朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘

在第一篇文章中我提到,在 SpringBoot 出現之前,我們需要使用 SpringMVC、Spring

Data、Spring Core 都需要對 Spring 內部的各種組件進行 Bean 以及 Bean 依賴的配置,在

90%的時候我們用的是默認的配置,不會自定義任何擴展類,這個時候也需要由使用者來

手動配置顯然不合理,有了 SpringBoot,我們只需引入啟動器依賴,然後啟動器就可以自

己做為自己的一些內部組件做自動配置,大大方便了使用者。啟動器的實現非常簡單,我

們來看下實現過程。

首先創建一個 Maven 空項目,引入 SpringBoot:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

me.josephzhu

spring101

0.0.1-SNAPSHOT

jar

spring101

org.springframework.boot

spring-boot-starter-parent

2.0.5.RELEASE

UTF-8

UTF-8

1.8

org.springframework.boot

spring-boot-starter

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

org.springframework.boot

spring-boot-maven-plugin

然後我們創建一個 Starter 模塊隸屬於父項目:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

me.josephzhu

spring101-customstarter

0.0.1-SNAPSHOT

jar

spring101-customstarter

me.josephzhu

spring101

0.0.1-SNAPSHOT

org.springframework.boot

spring-boot-autoconfigure

接下去我們創建一個服務抽象基類和實現,這個服務非常簡單,會依賴到一些屬性,然後

會有不同的實現(無參構造函數設置了打招呼用語的默認值為 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() {

this ("Hello");

}

@Autowired

protected MyServiceProperties properties;

public abstract String hello();

}

這裡注入了自定義屬性類:

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;

}

這裡看到了如果我們需要定義一個自定義類來關聯配置源(比如 application.properties 文

件配置)是多麼簡單,使用@ConfigurationProperties 註解標註我們的 POJO 告知註解我們

配置的前綴即可。額外提一句,如果希望我們的 IDE 可以針對自定義配置有提示的話(自

動完成,而且帶上註解中的提示語),可以引入如下的依賴:

org.springframework.boot

spring-boot-configuration-processor

true

這樣編譯後就會在 META-INF 下面生成一個叫做 spring-configuration-metadata.json 的文

件:

{

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

}

]

}

之後在使用配置的時候就可以有提示:

朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘

我們先來寫第一個服務實現,如下,只是輸出一下使用到的一些自定義屬性:

package me.josephzhu.spring101customstarter;

import org.springframework.stereotype.Service;

@Service

public class MyService extends AbstractMyService {

public MyService(String word) {

super(word);

}

public MyService(){}

@Override

public String hello() {

return String.format("V1 %s >> %s:%s !!", word, properties.getName(), properties.getAge());

}

}

關鍵的一步來了,接下去我們需要定義自動配置類:

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(){

return new MyService();

}

}

通過 EnableConfigurationProperties 來告訴 Spring 我們需要關聯一個配置文件配置類(配

置類鬥不需要設置@Component),通過@Configuration 告訴 Spring 這是一個 Bean 的配

置類,下面我們定義了我們 Service 的實現。

最後,我們需要告訴 SpringBoot 如何來找到我們的自動配置類,在合適的時候自動配置。

我們需要在項目資源目錄建一個 META-INF 文件夾,然後創建一個 spring.factories 文件,

寫入下面的內容:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=me.josephzhu.spring101customstarter.MyAutoCo

nfiguration

好了,就是這麼簡單,接下去我們創建一個項目來使用我們的自定義啟動器來試試:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

me.josephzhu

spring101-main

0.0.1-SNAPSHOT

jar

spring101-main

me.josephzhu

spring101

0.0.1-SNAPSHOT

me.josephzhu

spring101-customstarter

0.0.1-SNAPSHOT

org.springframework.boot

spring-boot-maven-plugin

創建一個 Runner 來調用服務:

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());

}

}

創建主程序:

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);

}

}

然後在 main 模塊的資源目錄下創建 application.properties 文件,寫入兩個配置:

spring101.age=35

spring101.name=zhuye

運行後可以看到輸出:

2018-09-30 14:55:00.848 INFO 12704 --- [ main]

me.josephzhu.spring101main.Runner1 : V1 Hello >> zhuye:35 !!

可以證明,第一我們的 main 模塊引入的 starter 正確被 SpringBoot 識別加載,第二 starter

中的 Configuration 正確執行不但加載了配置類,而且也正確注入了 Service 的一個實現。

如何實現條件配置?

作為組件的開發者,我們有的時候希望針對環境、配置、類的加載情況等等進行各種更智

能的自動配置,這個時候就需要使用 Spring 的 Conditional 特性。我們來看一個例子,如

果我們的 Service 隨著發展演化出了 v2 版本,我們希望用戶在默認的時候使用 v1,如果需

要的話可以進行 version 屬性配置允許用戶切換到 v2 版本。實現起來非常簡單,首先定義

另一個 v2 版本的服務:

package me.josephzhu.spring101customstarter;

import org.springframework.stereotype.Service;

@Service

public class MyServiceV2 extends AbstractMyService {

public MyServiceV2(String word) {

super(word);

}

public MyServiceV2(){}

@Override

public String hello() {

return String.format("V2 %s >> %s:%s !!", word, properties.getName(), properties.getAge());

}

}

和版本 v1 沒有任何區別,只是標記了一下 v2 關鍵字。

然後我們改造一下我們的自動配置類:

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(){

return new MyService();

}

@Bean

@ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v2")

MyServiceV2 getMyServiceV2(){

return new MyServiceV2();

}

}

這裡主要是為兩個 Bean 分別添加了@ConditionalOnProperty 註解,註解是自解釋的。這

裡說了如果 version 的值是 v1 或沒有定義 version 的話匹配到默認的 v1 版本的服務,如果

配置設置為 v2 的話匹配到 v2 版本的服務,就這麼簡單。

再來看一個例子,如果我們的使用者希望自己定義服務的實現,這個時候我們需要覆蓋自

動配置為我們自動裝配的 v1 和 v2,可以使用另一個註解@ConditionalOnMissingBean 來

告知 SpringBoot,如果找不到 Bean 的話再來自動配置:

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(){

return new MyService();

}

@Bean

@ConditionalOnMissingBean(MyServiceV2.class)

@ConditionalOnProperty(prefix = "spring101", name = "version", havingValue = "v2")

MyServiceV2 getMyServiceV2(){

return new MyServiceV2();

}

}

這樣的話,如果客戶端自己定義了 Service 的實現的話,就可以讓自動配置放棄自動配置使

用客戶端自己定義的 Bean。還有 N 多的 Conditional 註解可以使用,甚至可以自定義條

件,具體可以查看官方文檔。

進行一下測試

接下去,我們來寫一下單元測試來驗證一下我們之前的代碼,使用

ApplicationContextRunner 可以方便得設置帶入的各種外部配置項以及自定義配置類:

在這裡我們寫了三個測試用例:

 在提供了合適的屬性配置後,可以看到服務的輸出正確獲取到了屬性。

 使用配置項 version 來切換服務的實現,在省略 version,設置 version 為 1,設置

version 為 2 的情況下得到正確的輸出,分別是 v1、v1 和 v2。

 在客戶端自定義實現(MyServiceConfig)後可以看到並沒有加載使用自動配置裡定義

的服務實現,最後輸出了打招呼用語 Hi 而不是 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() {

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() {

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() {

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() {

return new MyService("Hi");

}

}

運行測試可以看到三個測試都可以通過,控制檯也輸出了 hello 方法的返回值:

朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘

實現自定義的配置數據源

接下去我們來看一下如何利用 EnvironmentPostProcessor 來實現一個自定義的配置數據

源。我們在 starter 項目中新建一個類,這個類使用了一個 Yaml 配置源加載器,然後我們

把加載到的自定義的 PropertySource 加入到 PropertySource 候選列表的第一個,這樣就可

以實現屬性優先從我們定義的(classpath 下的)config.yml 來讀取:

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);

}

private PropertySource> 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);

}

}

}

最關鍵的一步就是讓 SpringBoot 能加載到我們這個 PostProcessor,還是老樣子,在

spring,factories 文件中加入一項配置即可:

org.springframework.boot.env.EnvironmentPostProcessor=me.josephzhu.spring101customstarter.MyPropertySource

EnvironmentPostProcessor

現在,我們可以在 starter 項目下的 resrouces 目錄下創建一個 config.yml 來驗證一下:

spring101:

name: zhuye_yml

重新運行 main 項目可以看到如下的輸出結果中包含了 yml 字樣:

2018-09-30 15:27:05.123 INFO 12769 --- [ main] me.josephzhu.spring101main.Runner1 : V1

Hello >> zhuye_yml:35 !!

我們可以為項目添加一下 Actuator 模塊進行進一步驗證:

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

在配置文件中加入設置來放開所有的端口訪問:

management.endpoints.web.exposure.include=*

然後打開瀏覽器訪問 http://127.0.0.1:8080/actuator/env:

朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘

可以看到,的確是添加了我們自定義的 config.yml 作為 PropertySource。

自動配置的調試問題

對於複雜的項目,如果我們發現自動配置不起作用,要搞清楚框架是如何在各種條件中做

自動配置以及自動配置的匹配過程是比較麻煩的事情,這個時候我們可以打開 SpringBoot

的 Debug 來查看日誌:

朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘

我們可以在日誌中搜索我們關注的類型的匹配情況,是不是很直觀呢:

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)

我們可以試試在出錯的時候系統給我們的提示,來把配置文件中的 version 設置為 3:

spring101.version=v3

重新運行後看到如下輸出:

***************************

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.

這個所謂的分析報告是比較機械性的,作為框架的開發者,我們甚至可以自定義叫做

FailureAnalyzer 的東西來做更明確的提示。實現上和之前的步驟幾乎一樣,首先自定義一

個類:

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 {

@Override

protected FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) {

if(cause.getBeanType().equals(AbstractMyService.class))

return new FailureAnalysis("加載 MyService失敗", "請檢查配置文件中的 version屬性設置是否是 v1或

v2", rootFailure);

return null;

}

}

這裡我們根據 cause 的 Bean 類型做了簡單判斷,如果發生錯誤的是我們的 Service 類型的

話,告知使用者明確的錯誤原因(Description)以及怎麼來糾正這個錯誤(Action)。

然後老規矩,在 spring.factories 中進行關聯:

org.springframework.boot.diagnostics.FailureAnalyzer=me.josephzhu.spring101customstarter.MyFailureAnalyzer

重新運行程序後可以看到如下的結果:

***************************

APPLICATION FAILED TO START

***************************

Description:

加載 MyService 失敗

Action:

請檢查配置文件中的 version 屬性設置是否是 v1 或 v2

是不是直觀很多呢?這裡我的實現比較簡單,在正式的實現中你可以根據上下文以及環境

的各種情況為用戶進行全面的分析,分析服務啟動失敗的原因。

SpringBoot 的擴展點

在之前的幾個例子中,我們進行了各種擴展配置,通過 spring.factories 進行了自動配置、

環境後處理器配置以及錯誤分析器配置:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=me.josephzhu.spring101customstarter.MyAutoCo

nfiguration

org.springframework.boot.env.EnvironmentPostProcessor=me.josephzhu.spring101customstarter.MyPropertySource

EnvironmentPostProcessor

org.springframework.boot.diagnostics.FailureAnalyzer=me.josephzhu.spring101customstarter.MyFailureAnalyzer

其實,SpringBoot 還有一些其它的擴展槽,如下是 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.ReactiveOAuth2ResourceServerAutoConf

iguration,\

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

們可以看到一共有 8 大類的擴展槽,這些槽貫穿了整個 SpringBoot 加載的整個過程,感

興趣的讀者可以逐一搜索查看 Spring 的文檔和源碼進行進一步的分析。

本文從如何做一個 Starter 實現自動配置開始,進一步闡述瞭如何實現智能的條件配置,如

何,如何進行自動配置的測試,然後我們自定義了環境後處理器來加載額外的配置源(你

當然可以實現更復雜的配置源,比如從 Redis 和數據庫中獲取配置)以及通過開啟 Actutor

來驗證,定義了配置錯誤分析器來給用戶明確的錯誤提示。最後,我們看了一下

spring.factories 中的內容瞭解了 SpringBoot 內部定義的一些擴展槽和實現。


分享到:


相關文章: