Spring Boot 自動配置的原理、核心注解以及利用自動配置實現(xiàn)了

2.1 快速入門工程

第一章的 HelloBookController 控制層中,在代碼中以硬編碼的方式使用字符串表示書信息。下面把書的信息作為屬性,外化配置在 application.properties 。好處是將應(yīng)用參數(shù)、業(yè)務(wù)參數(shù)或第三方參數(shù)等統(tǒng)一配置在應(yīng)用配置文件中,避免配置侵入業(yè)務(wù)代碼,達(dá)到可配置的方式,方便及時調(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:《SpringBoot2.x核心技術(shù)實戰(zhàn)-上基礎(chǔ)篇》? ? ? ? writer:泥瓦匠BYSocket

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

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

注意:

在 application.properties 配置中文值,讀取時會出現(xiàn)中文亂碼問題。因為 Java .properties 文件默認(rèn)編碼方式是 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 來快速地進(jìn)行轉(zhuǎn)義。該工具有在線版實現(xiàn),地址如下:

https://javawind.net/tools/native2ascii.jsp

2.1.2 創(chuàng)建屬性類

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

importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;/**

* 書屬性

*/@ComponentpublicclassBookProperties{/**? ? * 書名? ? */@Value("${demo.book.name}")? ? privateStringname;/**? ? * 作者? ? */@Value("${demo.book.writer}")? ? privateStringwriter;// ... 省略 getter / setter 方法}

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

核心注解的知識點:

@Component 注解:

@Component 對類進(jìn)行標(biāo)注,職責(zé)是泛指組件 Bean ,應(yīng)用啟動時會被容器加載并加入容器管理。常見的 @Controller、@Service 、@Repository 是 @Component 的分類細(xì)化組件,分別對應(yīng)控制層、服務(wù)層、持久層的 Bean。

@Value 注解:

@Value 對 Bean 的字段或者方法參數(shù)進(jìn)行標(biāo)注,職責(zé)是基于表達(dá)式給字段或方法參數(shù)設(shè)置默認(rèn)屬性值。通常格式是注解 + SpEL 表達(dá)式,如 @Value("SpEL 表達(dá)式")。

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

2.1.3 獲取屬性

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

importdemo.springboot.config.BookProperties;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloBookController {? ? @AutowiredBookProperties bookProperties;? ? @GetMapping("/book/hello")? ? public String sayHello() {return"Hello, " +bookProperties.getWriter() + "iswriting"? ? ? ? ? ? ? ? +bookProperties.getName() + " !";? ? }}

通過 @Autowired 注解標(biāo)記在 BookProperties 字段,控制層自動裝配屬性 Bean 并使用。默認(rèn)情況下要求被注解的 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é)介紹。單元測試代碼如下:

importdemo.springboot.config.BookProperties;importorg.junit.Assert;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.context.junit4.SpringRunner;@RunWith(SpringRunner.class)@SpringBootTestpublicclassConfigApplicationTests{@AutowiredBookPropertiesbookProperties;@Testpublicvoid 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ù)進(jìn)行標(biāo)注,職責(zé)是基于表達(dá)式給字段或方法參數(shù)設(shè)置默認(rèn)屬性值。通常格式是注解 + SpEL 表達(dá)式,如 @Value("SpEL 表達(dá)式"),并標(biāo)注在對應(yīng)的字段或者方法上方,且必須對變量一一標(biāo)注。這種方式適用于小而不復(fù)雜的屬性結(jié)構(gòu)。屬性結(jié)構(gòu)復(fù)雜,字段很多的情況下,這種方式會比較繁瑣,應(yīng)該考慮使用 @ConfigurationProperties 注解。

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

importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.PropertySource;importorg.springframework.stereotype.Component;/**

* 書屬性

*/@Component@PropertySource("classpath:book.properties")publicclassBookProperties{/**? ? * 書名? ? */@Value("${demo.book.name}")? ? privateStringname;/**? ? * 作者? ? */@Value("${demo.book.writer}")? ? privateStringwriter;// ... 省略 getters / setters 方法}

2.2.2 @ConfigurationProperties 注解

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

importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;/**

* 書屬性

*

*/@Component@ConfigurationProperties(prefix="demo.book")publicclassBookComponent{/**

? ? * 書名

? ? */privateStringname;/**

? ? * 作者

? ? */privateStringwriter;// ... 省略 getters / setters 方法}

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

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

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

@AutowiredBookComponent bookComponent;@Testpublic 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

布爾值,默認(rèn) false。綁定對象時,忽略無效字段。

ignoreUnknownFields

布爾值,默認(rèn) true。綁定對象時,忽略未知字段。

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

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

importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.stereotype.Component;importorg.springframework.validation.annotation.Validated;importjavax.validation.constraints.NotEmpty;importjavax.validation.constraints.NotNull;/**

* 書屬性

*

*/@Component@ConfigurationProperties(prefix="demo.book")@ValidatedpublicclassBookComponent{/**

? ? * 書名

? ? */@NotEmptyprivateStringname;/**

? ? * 作者

? ? */@NotNullprivateStringwriter;// ... 省略 getters / setters 方法}

龍華大道1號http://www.kinghill.cn/Dynamics/2106.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。