Spring Boot 自動配置的原理档冬、核心注解以及利用自動配置實現(xiàn)了自定義 Starter 組件

本章內(nèi)容

  1. 自定義屬性快速入門
  2. 外化配置
  3. 自動配置
  4. 自定義創(chuàng)建 Starter 組件

摘錄:讀書是讀完這些文字還要好好用心去想想酷誓,寫書也一樣,做任何事也一樣

圖 2 第二章目錄結(jié)構(gòu)圖

第 2 章 Spring Boot 配置

Spring Boot 配置娘扩,包括自動配置和外化配置涮阔。本章先實現(xiàn)自定義屬性工程掰邢,將屬性外化配置在 application.properties 應(yīng)用配置文件辣之,然后在工程中獲取該屬性值怀估。接著會詳細介紹屬性的獲取方式歧蕉、外化配置和自動配置。最后會介紹利用自動配置自定義 Start 組件康铭。

2.1 快速入門工程

第一章的 HelloBookController 控制層中惯退,在代碼中以硬編碼的方式使用字符串表示書信息。下面把書的信息作為屬性从藤,外化配置在 application.properties 催跪。好處是將應(yīng)用參數(shù)、業(yè)務(wù)參數(shù)或第三方參數(shù)等統(tǒng)一配置在應(yīng)用配置文件中懊蒸,避免配置侵入業(yè)務(wù)代碼鳖孤,達到可配置的方式,方便及時調(diào)整修改。

2.1.1 配置屬性

新建工程命名為 chapter-2-spring-boot-config 忍燥,在 application.properties 中配置書名和作者队丝,配置如下:

## 書信息
demo.book.name=[Spring Boot 2.x Core Action]
demo.book.writer=BYSocket

.properties 文件的每行參數(shù)被存儲為一對字符串臭墨,即一個存儲參數(shù)名稱叶圃,被稱為鍵码党;另一個為值。一般稱為鍵值對配置。井號(#)或者英文狀態(tài)下的嘆號(!)作為第一行中第一個非空字符來表示該行的文本為注釋。另外斩熊,反斜杠(\)用于轉(zhuǎn)義字符霸株。

Spring Boot 支持并推薦使用 YAML 格式的配置文件,將 application.properties 文件替換成 application.yml 文件幼衰,并配置相同的屬性,配置如下:

## 書信息
demo:
    book:
        name: 《Spring Boot 2.x 核心技術(shù)實戰(zhàn) - 上 基礎(chǔ)篇》
        writer: 泥瓦匠BYSocket

YAML 是一個可讀性高,用來表達數(shù)據(jù)序列的格式。表示鍵值對格式時,注意鍵和值由冒號及空白字符分開。強調(diào)下,空白字符是必須的,IDE 一般也會提示休偶。兩種配置方式都非常便捷碱妆,在開發(fā)中選擇 .properties 或 .yml 文件配置纳本。但如果兩種配置文件同時存在的時候,默認優(yōu)先使用 .properties 配置文件。YAML 與 .properties 配置文件對比如圖 2-1 所示:

圖 2-1 YAML 與 .properties 配置文件對比

注意:

在 application.properties 配置中文值幌墓,讀取時會出現(xiàn)中文亂碼問題袭祟。因為 Java .properties 文件默認編碼方式是 iso-8859 ,Spring Boot 應(yīng)用以 UTF-8 的編碼方式讀取欧募,就導(dǎo)致出現(xiàn)亂碼問題。

官方 Issue 中的解決方法是金吗,將 .properties 文件中配置的中文值轉(zhuǎn)義成 Unicode 編碼形式十兢。例如 demo.book.writer=泥瓦匠 應(yīng)該配置成 demo.book.writer=\u6ce5\u74e6\u5320 趣竣。利用 IDEA properties 插件 或利用 Java 文件轉(zhuǎn)碼工具 native2ascii 來快速地進行轉(zhuǎn)義。該工具有在線版實現(xiàn)旱物,地址如下:
https://javawind.net/tools/native2ascii.jsp

2.1.2 創(chuàng)建屬性類

在工程中新建包目錄 demo.springboot.config 遥缕,并在目錄中創(chuàng)建名為 BookProperties 的屬性類,代碼如下:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 書屬性
 */
@Component
public class BookProperties {

    /**
     * 書名
     */
    @Value("${demo.book.name}")
    private String name;

    /**
     * 作者
     */
    @Value("${demo.book.writer}")
    private String writer;

    // ... 省略 getter / setter 方法
}

利用 @Component 注解定義了書的屬性 Bean,并通過 @Value 注解為該 Bean 的成員變量(或者方法參數(shù))自動注入 application.properties 文件的屬性值莉兰。@Value 注解是通過 “${propName}” 的形式引用屬性,propName 表示屬性名稱。

核心注解的知識點:

  • @Component 注解:
    @Component 對類進行標注九串,職責(zé)是泛指組件 Bean 烤低,應(yīng)用啟動時會被容器加載并加入容器管理。常見的 @Controller笆载、@Service 扑馁、@Repository@Component 的分類細化組件,分別對應(yīng)控制層凉驻、服務(wù)層腻要、持久層的 Bean。

  • @Value 注解:
    @Value 對 Bean 的字段或者方法參數(shù)進行標注涝登,職責(zé)是基于表達式給字段或方法參數(shù)設(shè)置默認屬性值雄家。通常格式是注解 + SpEL 表達式,如 @Value("SpEL 表達式")缀拭。

使用 @Vlaue 注解來引用屬性值時咳短,確保所引用的屬性值在 application.properties 文件存在并且相對應(yīng)匹配,否則會造成 Bean 的創(chuàng)建錯誤蛛淋,引發(fā) java.lang.IllegalArgumentException 非法參數(shù)異常咙好。

2.1.3 獲取屬性

修改原有的 HelloBookController 類,通過注入的方式獲取書屬性 Bean 并返回褐荷。代碼如下:

import demo.springboot.config.BookProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloBookController {

    @Autowired
    BookProperties bookProperties;

    @GetMapping("/book/hello")
    public String sayHello() {
        return "Hello勾效, " + bookProperties.getWriter() + " is writing "
                + bookProperties.getName() + " !";
    }
}

通過 @Autowired 注解標記在 BookProperties 字段叛甫,控制層自動裝配屬性 Bean 并使用层宫。默認情況下要求被注解的 Bean 必須存在,需要允許 NULL 值其监,可以設(shè)置其 required 屬性為 false: @Autowired(required = false)萌腿。

2.1.4 運行工程

執(zhí)行 ConfigApplication 類啟動,在控制臺看到成功運行的輸出后抖苦,打開瀏覽器訪問 /book/hello 地址毁菱,可以看到如圖 2-2 所示的返回結(jié)果:

圖 2-2 Hello Book 頁面

也可以通過單元測試的方式驗證屬性獲取是否成功米死,單元測試具體相關(guān)的會在第 9 章節(jié)介紹。單元測試代碼如下:

import demo.springboot.config.BookProperties;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ConfigApplicationTests {
    @Autowired
    BookProperties bookProperties;

    @Test
    public void testBookProperties() {
        Assert.assertEquals(bookProperties.getName(),"'Spring Boot 2.x Core Action'");
        Assert.assertEquals(bookProperties.getWriter(),"BYSocket");
    }
}

2.2 配置屬性的獲取方式

配置屬性的常用獲取方式有基于 @Value@ConfigurationProperties 注解兩種方式贮庞。兩種方式適合的場景不同峦筒,下面具體介紹其使用方法和場景。

2.2.1 @Value 注解

@Value 注解對 Bean 的變量或者方法參數(shù)進行標注窗慎,職責(zé)是基于表達式給字段或方法參數(shù)設(shè)置默認屬性值物喷。通常格式是注解 + SpEL 表達式,如 @Value("SpEL 表達式")遮斥,并標注在對應(yīng)的字段或者方法上方峦失,且必須對變量一一標注。這種方式適用于小而不復(fù)雜的屬性結(jié)構(gòu)术吗。屬性結(jié)構(gòu)復(fù)雜宠进,字段很多的情況下,這種方式會比較繁瑣藐翎,應(yīng)該考慮使用 @ConfigurationProperties 注解。

另外通過 @PropertySource 注解引入對應(yīng)路徑的其他 .properties 文件实幕。將書信息重新配置在 classpath 下新的 book.properties 配置文件后吝镣,讀取新配置文件的代碼如下:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

/**
 * 書屬性
 */
@Component
@PropertySource("classpath:book.properties")
public class BookProperties {

    /**
     * 書名
     */
    @Value("${demo.book.name}")
    private String name;

    /**
     * 作者
     */
    @Value("${demo.book.writer}")
    private String writer;

    // ... 省略 getters / setters 方法
}

2.2.2 @ConfigurationProperties 注解

在包目錄 demo.springboot.config 中創(chuàng)建名為 BookComponent 的屬性類,并使用 @ConfigurationProperties 注解獲取屬性昆庇,代碼如下:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 書屬性
 *
 */
@Component
@ConfigurationProperties(prefix = "demo.book")
public class BookComponent {

    /**
     * 書名
     */
    private String name;

    /**
     * 作者
     */
    private String writer;

    // ... 省略 getters / setters 方法
}

類似 @Value 注解方式末贾,使用 @ConfigurationProperties(prefix = "demo.book") 注解標注在類上方可以達到相同的效果。 @ConfigurationProperties 注解的 prefix 是指定屬性的參數(shù)名稱整吆。會匹配到配置文件中 “ demo.book.* ” 結(jié)構(gòu)的屬性拱撵,星號 “ * ” 是指會一一對應(yīng)匹配 BookComponent 類的字段名。例如表蝙,字段 name 表示書名拴测,會匹配到 demo.book.name 屬性值。

@Value 注解方式強制字段必須對應(yīng)在配置文件府蛇, @ConfigurationProperties 注解方式則不是必須的集索。一般情況下,所有字段應(yīng)該保證一一對應(yīng)在配置文件汇跨。如果沒有屬性值對應(yīng)的話务荆,該字段默認為空, @ConfigurationProperties 注解方式也不會引發(fā)任何異常穷遂,Spring Boot 推薦使用 @ConfigurationProperties 注解方式獲取屬性函匕。

同樣使用單元測試驗證獲取屬性是否成功。單元測試代碼如下:

@Autowired
BookComponent bookComponent;

@Test
public void testBookComponent() {
    Assert.assertEquals(bookComponent.getName(),"'Spring Boot 2.x Core Action'");
    Assert.assertEquals(bookComponent.getWriter(),"BYSocket");
}
API org.springframework.boot.context.properties.ConfigurationProperties 注解參數(shù)
  • prefix
    字符串值蚪黑,綁定該名稱前綴的屬性對象盅惜。
  • value
    字符串值中剩,功能同 prefix 參數(shù)。
  • ignoreInvalidFields
    布爾值酷窥,默認 false咽安。綁定對象時,忽略無效字段蓬推。
  • ignoreUnknownFields
    布爾值妆棒,默認 true。綁定對象時沸伏,忽略未知字段糕珊。

2.2.3 @ConfigurationProperties 數(shù)據(jù)驗證

@ConfigurationProperties 注解方式支持驗證功能,即當屬性類被 @Validated 注解標注時毅糟,Spring Boot 初始化時會驗證類的字段红选。在類的字段上添加 JSR-303 約束注解,進行數(shù)據(jù)驗證姆另。下面為書屬性字段添加非 NULL 和字符串非空約束喇肋,代碼如下:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

/**
 * 書屬性
 *
 */
@Component
@ConfigurationProperties(prefix = "demo.book")
@Validated
public class BookComponent {

    /**
     * 書名
     */
    @NotEmpty
    private String name;

    /**
     * 作者
     */
    @NotNull
    private String writer;

    // ... 省略 getters / setters 方法
}

通過 @Validated 注解開啟對 BookComponent 類字段的數(shù)據(jù)驗證,如果 name 字段為 NULL 或者為空字符串時迹辐,會引發(fā) BindValidationException 綁定數(shù)據(jù)驗證異常蝶防。數(shù)據(jù)驗證常用在郵箱格式或者有長度限制的屬性字段。另外明吩,驗證嵌套屬性的值间学,必須在嵌套對象字段上方標注 @Valid 注解,用來觸發(fā)其驗證印荔。例如低葫,在書屬性中新增嵌套對象出版社 Publishing,就需要在該對象上方標注 @Valid 注解仍律,來開啟對 Publishing 對象的數(shù)據(jù)驗證嘿悬。綜上,兩種屬性獲取方式各有優(yōu)缺點水泉,對比如圖 2-3 所示:

圖 2-3 @ConfigurationPropertiesd vs @Value

2.3 外化配置

Spring Boot 可以將配置外部化鹊漠,即分離存儲在 classpath 之外,這種模式叫做 “外化配置”茶行。常用在不同環(huán)境中躯概,將配置從代碼中分離外置,只要簡單地修改下外化配置畔师,可以依舊運行相同的應(yīng)用代碼娶靡。外化配置表現(xiàn)形式不單單是 .properties 和 .yml 屬性文件,還可以使用環(huán)境變量和命令行參數(shù)等來實現(xiàn)看锉。那么姿锭,多處配置了相同屬性時塔鳍,Spring Boot 是通過什么方式來控制外化配置的沖突呢?答案是外化配置優(yōu)先級呻此。

2.3.1 外化配置優(yōu)先級

用命令行配置去覆蓋 .properties 文件配置方法很簡單轮纫。正常情況下利用 Java 命令運行工程,代碼如下:

// chapter-2-spring-boot-config 目錄下運行
java -jar target/chapter-2-spring-boot-config-1.0.jar

下面將書的作者信息 BYSocket 改成 Jeff , 通過命令行配置覆蓋屬性,代碼如下:

java -jar target/chapter-2-spring-boot-config-1.0.jar --demo.book.writer=Jeff

在命令行配置中比肄,設(shè)置屬性值的格式是用兩個連續(xù)的減號 “--”標志屬性。在控制臺看到成功運行的輸出后糯彬,打開瀏覽器,訪問 /book/hello 地址葱她,可以看到如圖 2-4 所示的返回結(jié)果:

圖 2-4 書信息被覆蓋頁面

通過命令行配置覆蓋屬性提供了非常大的作用與便利性撩扒,常見于使用 shell 腳本運行工程時,可以方便地修改工程運行的配置吨些。

但是這就引發(fā)了一個問題搓谆,豈不讓工程很有侵入性,如果開放這個功能豪墅,導(dǎo)致未知的安全問題挽拔。所以 Spring Boot 提供了屏蔽命令行屬性值設(shè)置,在應(yīng)用啟動類中設(shè)置 setAddCommandLineProperties 方法為 false 但校,用于關(guān)閉命令行配置功能,代碼如下:

SpringApplication.setAddCommandLineProperties(false);

命令行配置屬性的優(yōu)先級是第四啡氢。外化配置獲取屬性時状囱,會按優(yōu)先級從高到低獲取。如果高優(yōu)先級存在屬性倘是,則返回屬性亭枷,并忽略優(yōu)先級低的屬性。優(yōu)先級如下:

  1. 本地 Devtools 全局配置
  2. 測試時 @TestPropertySource 注解配置
  3. 測試時 @SpringBootTest 注解的 properties 配置
  4. 命令行配置
  5. SPRING_APPLICATION_JSON 配置
  6. ServletConfig 初始化參數(shù)配置
  7. ServletContext 初始化參數(shù)配置
  8. Java 環(huán)境的 JNDI 參數(shù)配置
  9. Java 系統(tǒng)的屬性配置
  10. OS 環(huán)境變量配置
  11. 只能隨機屬性的 RandomValuePropertySource 配置
  12. 工程 jar 之外的多環(huán)境配置文件(application- {profile}.properties 或 YAML)
  13. 工程 jar 之內(nèi)的多環(huán)境配置文件(application- {profile}.properties 或 YAML)
  14. 工程 jar 之外的應(yīng)用配置文件(application.properties 或 YAML)
  15. 工程 jar 之內(nèi)的應(yīng)用配置文件(application.properties 或 YAML)
  16. @Configuration 類中的 @PropertySource 注解配置
  17. 默認屬性配置(SpringApplication.setDefaultProperties 指定)

2.3.2 屬性引用

在 application.properties 中配置屬性時搀崭,屬性之間可以直接通過 “${propName}” 的形式引用其他屬性叨粘。比如新增書的描述 description 屬性,代碼如下:

## 書信息
demo.book.name=[Spring Boot 2.x Core Action]
demo.book.writer=BYSocket
demo.book.description=${demo.book.writer}'s${demo.book.name}

demo.book.description 屬性引用了前面定義的 demo.book.name 和 demo.book.writer 屬性瘤睹,其值為 BYSocket's[Spring Boot 2.x Core Action] 升敲。一方面可以使相同配置可以復(fù)用,另一方面增強了配置的閱讀性轰传。

2.3.3 使用隨機數(shù)

在 application.properties 中配置屬性時驴党,可以使用隨機數(shù)配置,例如注入某些密鑰获茬、UUID 或者測試用例港庄,需要每次不是一個固定的值倔既。RandomValuePropertySource 類隨機提供整形、長整形數(shù)鹏氧、UUID 或者字符串渤涌。使用代碼如下:

my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

2.3.4 多環(huán)境配置

多環(huán)境是指不同配置的生產(chǎn)服務(wù)器使用同一工程代碼部署,比如:開發(fā)環(huán)境把还、測試環(huán)境实蓬、預(yù)發(fā)環(huán)境、生產(chǎn)環(huán)境等笨篷。各個環(huán)境的工程端口瞳秽、數(shù)據(jù)庫配置、Redis 配置率翅、日志配置等都會不同练俐,傳統(tǒng)模式下需要修改配置,工程重新編譯打包冕臭,并部署到指定環(huán)境服務(wù)器腺晾。結(jié)果是容易發(fā)生配置錯誤,導(dǎo)致開發(fā)部署效率低下辜贵。Spring Boot 使用多環(huán)境配置去解決這個問題悯蝉。

多環(huán)境配置,類似 Maven 構(gòu)建配置文件的思路托慨,即配置多個不同環(huán)境的配置文件鼻由,再通過 spring.profiles.active 命令去指定讀取特定配置文件的屬性。多環(huán)境配置文件是不同于 application.properties 應(yīng)用配置文件厚棵。多環(huán)境配置文件的約定命名格式為 application-{profile}.properties蕉世。多環(huán)境配置功能默認為激活狀態(tài),如果其他配置未被激活婆硬,則 {profile} 默認為 default狠轻,會加載 application-default.properties 默認配置文件,沒有該文件就會加載 application.properties 應(yīng)用配置文件彬犯。

多環(huán)境配置文件的屬性讀取方式和從 application.properties 應(yīng)用配置文件讀取方式一致向楼。不管多環(huán)境配置文件在工程 jar 包內(nèi)還是包外,按照配置優(yōu)先級覆蓋其他配置文件谐区。在微服務(wù)實踐開發(fā)中湖蜕,經(jīng)常會使用一個類似 deploy 工程去管理配置文件和打包其他業(yè)務(wù)工程。

在 application.properties 同級目錄中宋列,新建 application-dev.properties 作為開發(fā)環(huán)境配置文件重荠,配置如下:

## 書信息
demo.book.name=[Spring Boot 2.x Core Action]  From Dev
demo.book.writer=BYSocket

新建 application-prod.properties 作為生產(chǎn)環(huán)境配置文件,代碼如下:

## 書信息
demo.book.name=<Spring Boot 2.x Core Action Dev> From Prod
demo.book.writer=BYSocket

通過命令行指定讀取 dev 環(huán)境配置文件并運行工程,代碼如下:

java -jar target/chapter-2-spring-boot-config-1.0.jar --spring.profiles.active=dev

在多個環(huán)境配置中戈鲁,通過命令 --spring.profiles.active=dev 指定讀取某個配置文件仇参,將 dev 更改成 prod ,輕松切換讀取生產(chǎn)環(huán)境配置婆殿。也可以在控制臺的日志中確定配置讀取來自 dev :

2017-11-09 12:10:52.978  INFO 72450 --- [           main] demo.springboot.ConfigApplication        : The following profiles are active: dev

最后打開瀏覽器诈乒,訪問 /book/hello 地址,可以看到如圖 2-5 所示的返回結(jié)果:

圖 2-5 dev 環(huán)境書信息頁面

2.4 自動配置

Spring Boot spring-boot-autoconfigure 依賴實現(xiàn)了默認的配置項婆芦,即應(yīng)用默認值怕磨。這種模式叫做 “自動配置”。Spring Boot 自動配置會根據(jù)添加的依賴消约,自動加載依賴相關(guān)的配置屬性并啟動依賴肠鲫。例如默認用的內(nèi)嵌式容器是 Tomcat ,端口默認設(shè)置為 8080或粮。

為什么需要自動配置导饲?顧名思義,自動配置的意義是利用這種模式代替了配置 XML 繁瑣模式氯材。以前使用 Spring MVC 渣锦,需要進行配置組件掃描、調(diào)度器氢哮、視圖解析器等袋毙,使用 Spring Boot 自動配置后,只需要添加 MVC 組件即可自動配置所需要的 Bean冗尤。所有自動配置的實現(xiàn)都在 spring-boot-autoconfigure 依賴中听盖,包括 Spring MVC 、Data 和其它框架的自動配置裂七。

2.4.1 spring-boot-autoconfigure 依賴

spring-boot-autoconfigure 依賴皆看,是 Spring Boot 實現(xiàn)自動配置的核心 Starter 組件。其實現(xiàn)源碼包結(jié)構(gòu)如圖 2-6 所示:

圖 2-6 spring-boot-autoconfigure 依賴包目錄

從圖中可以看出碍讯,其中常見核心的包如下:

org.springframework.boot.autoconfigure
org.springframework.boot.autoconfigure.data.jpa
org.springframework.boot.autoconfigure.thymeleaf
org.springframework.boot.autoconfigure.web.servlet
org.springframework.boot.autoconfigure.web.reactive
... 省略

在各自包目錄下有對應(yīng)的自動配置類,代碼如下:

JpaRepositoriesAutoConfiguration 
ThymeleafAutoConfiguration
WebMvcAutoConfiguration
WebFluxAutoConfiguration
... 省略

上面自動配置類依次是 Jpa 自動配置類扯躺、Thymeleaf 自動配置類捉兴、Web MVC 自動配置類和 WebFlux 自動配置類。WebFlux 響應(yīng)式框架會在第 3 章 詳細介紹使用录语。

spring-boot-autoconfigure 職責(zé)是通過 @EnableAutoConfiguration 核心注解倍啥,掃描 ClassPath 目錄中自動配置類對應(yīng)依賴,并按一定規(guī)則獲取默認配置并自動初始化所需要的 Bean澎埠。在 application.properties 配置文件也可以修改默認配置項虽缕,常用配置清單地址如下:

https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

2.4.2 @EnableAutoConfiguration 注解

自動配置工作機制是通過 @EnableAutoConfiguration 注解中 @ImportAutoConfigurationImportSelector 自動配置導(dǎo)入選擇器類實現(xiàn)的。查閱源碼可得具體流程如下:

  • AutoConfigurationImportSelector 通過 SpringFactoriesLoader.loadFactoryNames() 核心方法讀取 ClassPath 目錄下面的 META-INF/spring.factories 文件蒲稳。
  • spring.factories 文件中配置的 Spring Boot 自動配置類氮趋,例如常見的 WebMvcAutoConfiguration Web MVC 自動配置類和ServletWebServerFactoryAutoConfiguration 容器自動配置類 伍派。
  • spring.factories 文件和 application.properties 文件都屬于配置文件,配置的格式均為鍵值對剩胁。里面配置的每個自動配置類都會定義相關(guān) Bean 的實例配置诉植,也會定義什么條件下自動配置和哪些 Bean 被實例化。
  • 當 pom.xml 添加某 Starter 依賴組件的時候昵观,就會自動觸發(fā)該依賴的默認配置晾腔。

例如添加 spring-boot-starter-web 依賴后,啟動應(yīng)用會觸發(fā)容器自動配置類啊犬。容器自動配置類 ServletWebServerFactoryAutoConfiguration 的部分代碼如下:

package org.springframework.boot.autoconfigure.web.servlet;

@Configuration
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class})
public class ServletWebServerFactoryAutoConfiguration {
... 省略
}

上面代碼中灼擂,@ConditionalOnClass 注解表示對應(yīng)的 ServletRequest 類在 ClassPath 目錄下面存在,并且 @ConditionalOnWebApplication 注解表示該應(yīng)用是 Servlet Web 應(yīng)用時觉至,才會去啟動容器自動配置剔应,并通過 ServerProperties 類默認設(shè)置了端口為 8080。Type.SERVLET 枚舉代表 Servlet Web 應(yīng)用康谆,Type.REACTIVE 枚舉代表響應(yīng)式 WebFlux 應(yīng)用领斥。

自動配置,是一把雙刃劍沃暗。用好了就像天下武功唯快不破一樣月洛。但要注意一些自動化配置造成的問題。常見的問題有:

  • Spring Boot 工程添加某些 Starter 組件依賴孽锥,又不需要觸發(fā)組件自動配置
  • Spring Boot 配置多個不同數(shù)據(jù)源配置時嚼黔,使用 XML 配置多數(shù)據(jù)源,但其默認數(shù)據(jù)源配置會觸發(fā)自動配置出現(xiàn)問題惜辑。

類似場景下唬涧,解決方式是通過 exclude 屬性指定并排除自動配置類,代碼如下:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

也等價于配置 @EnableAutoConfiguration 注解盛撑,代碼如下:

@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})

自動配置會最大的智能化碎节,只有配置了 exclude 屬性時,Spring Boot 優(yōu)先初始化用戶定義的 Bean 抵卫,然后再進行自動配置狮荔。

2.4.3 利用自動配置自定義 Starter 組件

當公司需要共享或者開源 Spring Boot Starter 組件依賴包,就可以利用自動配置自定義 Starter 組件介粘。一個完整的 Starter 組件包括以下兩點:

  • 提供自動配置功能的自動配置模塊殖氏。
  • 提供依賴關(guān)系管理功能的組件模塊,即封裝了組件所有功能姻采,開箱即用雅采。

實現(xiàn)自定義 Starter 組件,并不會將這兩點嚴格區(qū)分,可以將自動配置功能和依賴管理結(jié)合在一起實現(xiàn)婚瓜。下面利用自動配置實現(xiàn)自定義 Starter 組件:spring-boot-starter-swagger 組件是用來快速生成 API 文檔宝鼓,簡化原生使用 Swagger2 。

spring-boot-starter-swagger 組件為 Spring For All 社區(qū)(spring4all.com)開源項目闰渔,源代碼地址是 https://github.com/SpringForAll/spring-boot-starter-swagger席函。

什么是 Swagger2

Swagger2 是 API 最大的開發(fā)框架,基于 OpenAPI 規(guī)范(OAS)冈涧,管理了 API 整個生命周期茂附,即從 API 設(shè)計到文檔,從測試到部署督弓。具體更多了解見其官網(wǎng)营曼,https://swagger.io

spring-boot-starter-swagger 組件依賴

創(chuàng)建一個新的 Spring Boot 工程愚隧,命名為 spring-boot-starter-swagger蒂阱。在 pom.xml 配置相關(guān)

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${version.swagger}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${version.swagger}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>${version.swagger}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.12</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    

配置中添加了 spring-boot-starter 組件依賴用于自動配置特性,springfox-swagger2 依賴是 Swagger2 框架狂塘。

Swagger2 屬性配置類 SwaggerProperties

新建名為 SwaggerProperties Swagger2 屬性配置類录煤,包含了所有默認屬性值。使用該組件時荞胡,可以在 application.properties 配置文件配置對應(yīng)屬性項妈踊,進行覆蓋默認配置。代碼如下:

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import springfox.documentation.schema.ModelRef;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {

    /**是否開啟swagger**/
    private Boolean enabled;

    /**標題**/
    private String title = "";
    /**描述**/
    private String description = "";
    /**版本**/
    private String version = "";
    /**許可證**/
    private String license = "";
    /**許可證URL**/
    private String licenseUrl = "";
    /**服務(wù)條款URL**/
    private String termsOfServiceUrl = "";

    private Contact contact = new Contact();

    /**swagger會解析的包路徑**/
    private String basePackage = "";

    /**swagger會解析的url規(guī)則**/
    private List<String> basePath = new ArrayList<>();
    /**在basePath基礎(chǔ)上需要排除的url規(guī)則**/
    private List<String> excludePath = new ArrayList<>();

    /**分組文檔**/
    private Map<String, DocketInfo> docket = new LinkedHashMap<>();

    /**host信息**/
    private String host = "";

    /**全局參數(shù)配置**/
    private List<GlobalOperationParameter> globalOperationParameters;

     ... 省略泪漂,具體代碼見 GitHub
}

@ConfigurationProperties(prefix = "swagger") 標注在類上方是指定屬性的參數(shù)名稱為 swagger廊营。會對應(yīng)匹配到配置文件中 “ swagger.* ” 結(jié)構(gòu)的屬性,例如萝勤,字段標題 title 表示標題露筒,會匹配到 swagger.title 屬性值。

Swagger2 自動配置類 SwaggerAutoConfiguration

新建名為 SwaggerAutoConfiguration Swagger2 自動配置類敌卓,提供 Swagger2 依賴關(guān)系管理功能和自動配置功能慎式。代碼如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

@Configuration
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
@Import({
        Swagger2DocumentationConfiguration.class,
        BeanValidatorPluginsConfiguration.class
})
public class SwaggerAutoConfiguration implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperties swaggerProperties() {
        return new SwaggerProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
    public List<Docket> createRestApi(SwaggerProperties swaggerProperties) {
        ... 省略,具體代碼見 GitHub
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

上面代碼實現(xiàn)流程如下:

  1. @Configuration 注解標注在類上方趟径,表明該類為配置類瘪吏。
  2. @Import 注解引入 Swagger2 提供的配置類 Swagger2DocumentationConfiguration 和 Bean 數(shù)據(jù)驗證插件配置類 BeanValidatorPluginsConfiguration
  3. @ConditionalOnMissingBean 注解標注了兩處方法舵抹,當 Bean 沒有被創(chuàng)建時會執(zhí)行被標注的初始化方法肪虎。第一處被標記方法是 swaggerProperties() 劣砍,用來實例化屬性配置類 SwaggerProperties;第二處被標記方法是 createRestApi()惧蛹, 用來實例化 Swagger2 API 映射的 Docket 列表對象。
  4. @ConditionalOnProperty 注解標注在 createRestApi() 方法,name 屬性會去檢查環(huán)境配置項 swagger.enabled 香嗓。默認情況下迅腔,屬性存在且不是 false 的情況下,會觸發(fā)該初始化方法靠娱。matchIfMissing 屬性默認值為 false 沧烈,這里設(shè)置為 true,表示如果環(huán)境配置項沒被設(shè)置像云,也會觸發(fā)锌雀。
Swagger2 啟動注解類 EnableSwagger2Doc

新建名為 EnableSwagger2Doc Swagger2 啟動注解類,用于開關(guān) spring-boot-starter-swagger 組件依賴迅诬。代碼如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({SwaggerAutoConfiguration.class})
public @interface EnableSwagger2Doc {


}

上面代碼 @Import 注解引入 Swagger2 自動配置類 SwaggerAutoConfiguration腋逆。當將該注解配置在應(yīng)用啟動類上方,即可開啟 Swagger2 自動配置及其功能侈贷。

使用 spring-boot-starter-swagger 組件依賴

上面簡單介紹了spring-boot-starter-swagger 組件的核心代碼實現(xiàn)惩歉,同樣使用方式也很簡單。在 chapter-2-spring-boot-config 工程的 Maven 配置中添加對應(yīng)的依賴配置俏蛮,目前支持 1.5.0.RELEASE 以上版本撑蚌,配置如下:

<!-- 自定義 swagger2 Starter 組件依賴 -->
<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>spring-boot-starter-swagger</artifactId>
    <version>2.0</version>
</dependency>

另外,需要在 ConfigApplication 應(yīng)用啟動類上方配置啟動注解類 EnableSwagger2Doc搏屑,代碼如下:

import com.spring4all.swagger.EnableSwagger2Doc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@EnableSwagger2Doc // 開啟 Swagger
@SpringBootApplication
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

執(zhí)行 ConfigApplication 類啟動争涌,在控制臺看到成功運行的輸出后,打開瀏覽器訪問 localhost:8080/swagger-ui.html 地址睬棚,可以看到自動生成的 Swagger API 文檔第煮,如圖 2-7 所示:

圖 2-7 Swagger API 文檔

API org.springframework.boot.autoconfigure.EnableAutoConfiguration 注解參數(shù)
  • exclude:
    Class 數(shù)組,排除特定的自動配置類抑党。
  • excludeName:
    字符串數(shù)組包警,排除特定名稱的自動配置類。
API org.springframework.boot.autoconfigure.ConditionalOnProperty 注解參數(shù)
  • havingValue:
    字符串底靠,屬性期望值是否匹配害晦。
  • matchIfMissing:
    布爾值,如果該屬性值未設(shè)置暑中,則匹配壹瘟。
  • name:
    字符串數(shù)組,要測試的屬性名鳄逾。
  • prefix:
    字符串稻轨,屬性前綴名。
  • value:
    字符串雕凹,功能同 name殴俱。
API org.springframework.boot.autoconfigure.ConditionalOnClass 注解參數(shù)
  • name:
    字符串數(shù)組政冻,類名必須存在。
  • value:
    Class 數(shù)組线欲,類必須存在明场。
API org.springframework.boot.autoconfigure.ConditionalOnMissingBean 注解參數(shù)
  • annotation:
    注解 Class 數(shù)組,匹配注解裝飾的 Bean李丰。
  • ignored:
    Class 數(shù)組苦锨,匹配時,忽略該類型的 Bean趴泌。
  • ignoredType:
    字符串數(shù)組舟舒,匹配時,忽略該類型名稱的 Bean嗜憔。
  • name:
    字符串數(shù)組魏蔗,匹配要檢查的 Bean 名稱。
  • search:
    SearchStrategy 對象痹筛,通過 SearchStrategy 來決定程序的上下文策略莺治。
  • type:
    字符串史胡族,匹配要檢查的 Bean 類型名稱帚稠。
  • value:
    Class 數(shù)組谣旁,匹配要檢查的 Bean 類型。
API org.springframework.boot.autoconfigure.ConditionalOnWebApplication 注解參數(shù)
  • type:
    ConditionalOnWebApplication.Type 對象滋早,匹配對應(yīng)的 Web 應(yīng)用程序類型榄审。

2.5 本章小結(jié)

本章從自定義屬性快速入門工程出發(fā),介紹了兩種配置文件以及屬性的獲取方式杆麸,然后講解了外化配置的優(yōu)先級搁进、屬性引用、隨機式使用和多環(huán)境配置昔头,最后講解了自動配置的原理饼问、核心注解以及利用自動配置實現(xiàn)了自定義 Starter 組件。下一章介紹 Spring Boot Web 開發(fā)相關(guān)揭斧。

本章示例代碼地址:https://github.com/JeffLi1993/springboot-core-action-book-demo/tree/master/chapter-2-spring-boot-config

本文由博客群發(fā)一文多發(fā)等運營工具平臺 OpenWrite 發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末演侯,一起剝皮案震驚了整個濱河市毡咏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌誓篱,老刑警劉巖兵多,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箩做,死亡現(xiàn)場離奇詭異棋恼,居然都是意外死亡萎坷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門成艘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赏半,“玉大人梅忌,你說我怎么就攤上這事〕疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵琼腔,是天一觀的道長瑰枫。 經(jīng)常有香客問我,道長丹莲,這世上最難降的妖魔是什么光坝? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮甥材,結(jié)果婚禮上盯另,老公的妹妹穿的比我還像新娘。我一直安慰自己洲赵,他們只是感情好鸳惯,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叠萍,像睡著了一般芝发。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苛谷,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天辅鲸,我揣著相機與錄音,去河邊找鬼腹殿。 笑死独悴,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的锣尉。 我是一名探鬼主播刻炒,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼自沧!你這毒婦竟也來了落蝙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤暂幼,失蹤者是張志新(化名)和其女友劉穎筏勒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旺嬉,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡管行,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了邪媳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捐顷。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡荡陷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出迅涮,到底是詐尸還是另有隱情废赞,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布叮姑,位于F島的核電站唉地,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏传透。R本人自食惡果不足惜耘沼,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朱盐。 院中可真熱鬧群嗤,春花似錦、人聲如沸兵琳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躯肌。三九已至赃绊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間羡榴,已是汗流浹背碧查。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留校仑,地道東北人忠售。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像迄沫,于是被迫代替她去往敵國和親稻扬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355