文本我們會一步一步做一個例子來看看 SpringBoot 的自動配置是如何實現的,然後來看一
些 SpringBoot 留給我們的擴展點。
自己製作一個 SpringBoot Starter
我們知道 SpringBoot 提供了非常多的啟動器,引入了啟動器依賴即可直接享受到自動依賴
配置和自動屬性配置:
![朱曄和你聊 Spring 系列 S1E2:SpringBoot 並不神秘](http://p2.ttnews.xyz/loading.gif)
在第一篇文章中我提到,在 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">
然後我們創建一個 Starter 模塊隸屬於父項目:
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
接下去我們創建一個服務抽象基類和實現,這個服務非常簡單,會依賴到一些屬性,然後
會有不同的實現(無參構造函數設置了打招呼用語的默認值為 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 可以針對自定義配置有提示的話(自
動完成,而且帶上註解中的提示語),可以引入如下的依賴:
這樣編譯後就會在 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 並不神秘](http://p2.ttnews.xyz/loading.gif)
我們先來寫第一個服務實現,如下,只是輸出一下使用到的一些自定義屬性:
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">
創建一個 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 方法的返回值:
實現自定義的配置數據源
接下去我們來看一下如何利用 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 模塊進行進一步驗證:
在配置文件中加入設置來放開所有的端口訪問:
management.endpoints.web.exposure.include=*
然後打開瀏覽器訪問 http://127.0.0.1:8080/actuator/env:
可以看到,的確是添加了我們自定義的 config.yml 作為 PropertySource。
自動配置的調試問題
對於複雜的項目,如果我們發現自動配置不起作用,要搞清楚框架是如何在各種條件中做
自動配置以及自動配置的匹配過程是比較麻煩的事情,這個時候我們可以打開 SpringBoot
的 Debug 來查看日誌:
我們可以在日誌中搜索我們關注的類型的匹配情況,是不是很直觀呢:
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 內部定義的一些擴展槽和實現。
閱讀更多 隨緣主人的園子 的文章