網(wǎng)站首頁 編程語言 正文
在編寫項目代碼時,我們要求更靈活的配置,更好的模塊化整合。在 Spring Boot 項目中,為滿足以上要求,我們將大量的參數(shù)配置在 application.properties 或 application.yml 文件中,通過?@ConfigurationProperties
?注解,我們可以方便的獲取這些參數(shù)值
使用 @ConfigurationProperties 配置模塊
假設(shè)我們正在搭建一個發(fā)送郵件的模塊。在本地測試,我們不想該模塊真的發(fā)送郵件,所以我們需要一個參數(shù)來「開關(guān)」 disable 這個功能。另外,我們希望為這些郵件配置一個默認(rèn)的主題,這樣,當(dāng)我們查看郵件收件箱,通過郵件主題可以快速判斷出這是測試郵件
在 application.properties 文件中創(chuàng)建這些參數(shù):
myapp.mail.enabled=true
myapp.mail.default-subject=This is a Test
我們可以使用?@Value
?注解或著使用 Spring?Environment
?bean 訪問這些屬性,是這種注入配置方式有時顯得很笨重。我們將使用更安全的方式(@ConfigurationProperties
?)來獲取這些屬性
@Data
@ConfigurationProperties(prefix = "myapp.mail")
public class MailModuleProperties{
private Boolean enabled = Boolean.TRUE;
private String defaultSubject;
}
@ConfigurationProperties
?的基本用法非常簡單:我們?yōu)槊總€要捕獲的外部屬性提供一個帶有字段的類。請注意以下幾點:
- 前綴定義了哪些外部屬性將綁定到類的字段上
- 根據(jù) Spring Boot 寬松的綁定規(guī)則,類的屬性名稱必須與外部屬性的名稱匹配
- 我們可以簡單地用一個值初始化一個字段來定義一個默認(rèn)值
- 類本身可以是包私有的
- 類的字段必須有公共 setter 方法
Spring 寬松綁定規(guī)則 (relaxed binding)
Spring使用一些寬松的綁定屬性規(guī)則。因此,以下變體都將綁定到 hostName 屬性上:
mail.hostName=localhost
mail.hostname=localhost
mail.host_name=localhost
mail.host-name=localhost
mail.HOST_NAME=localhost
如果我們將 MailModuleProperties 類型的 bean 注入到另一個 bean 中,這個 bean 現(xiàn)在可以以類型安全的方式訪問那些外部配置參數(shù)的值。
但是,我們?nèi)匀恍枰?Spring 知道我們的 @ConfigurationProperties 類存在,以便將其加載到應(yīng)用程序上下文中(?面試還不知道 BeanFactory 和 ApplicationContext 的區(qū)別?)
激活 @ConfigurationProperties
對于 Spring Boot,創(chuàng)建一個 MailModuleProperties 類型的 bean,我們可以通過下面幾種方式將其添加到應(yīng)用上下文中
首先,我們可以通過添加 @Component 注解讓 Component Scan 掃描到
@Component
@ConfigurationProperties(prefix ="myapp.mail")
class MailModuleProperties{
//....
}
?
很顯然,只有當(dāng)類所在的包被 Spring?@ComponentScan
?注解掃描到才會生效,默認(rèn)情況下,該注解會掃描在主應(yīng)用類下的所有包結(jié)構(gòu)
我們也可以通過 Spring 的 Java Configuration 特性實現(xiàn)同樣的效果:
@Configuration
class PropertiesConfig{
@Bean
public MaillModuleProperties maillModuleProperties(){
return new MailModuleProperties();
}
}
只要 MailModuleConfiguration 類被 Spring Boot 應(yīng)用掃描到,我們就可以在應(yīng)用上下文中訪問 MailModuleProperties bean
我們還可以使用?@EnableConfigurationProperties
?注解讓我們的類被 Spring Boot 所知道,在該注解中其實是用了@Import(EnableConfigurationPropertiesImportSelector.class)
?實現(xiàn),大家可以看一下
@Configuration
@EnableConfigurationProperties(MaillModuleProperties.class)
class PropertiesConfig{
}
激活一個 @ConfigurationProperties 類的最佳方式是什么?
所有上述方法都同樣有效。然而,我建議模塊化你的應(yīng)用程序,并讓每個模塊提供自己的
@ConfigurationProperties
?類,只提供它需要的屬性,就像我們在上面的代碼中對郵件模塊所做的那樣。這使得在不影響其他模塊的情況下重構(gòu)一個模塊中的屬性變得容易。因此,我不建議在應(yīng)用程序類本身上使用?
@EnableConfigurationProperties
,如許多其他教程中所示,是在特定于模塊的 @Configuration 類上使用@EnableConfigurationProperties
,該類也可以利用包私有的可見性對應(yīng)用程序的其余部分隱藏屬性。
無法轉(zhuǎn)換的屬性
如果我們在 application.properties 屬性上定義的屬性不能被正確的解析會發(fā)生什么?假如我們?yōu)樵緫?yīng)該為布爾值的屬性提供的值為 'foo':
myapp.mail.enabled=foo
默認(rèn)情況下,Spring Boot 將會啟動失敗,并拋出異常:
Failed to bind properties under 'myapp.mail.enabled' to java.lang.Boolean:
Property: myapp.mail.enabled
Value: foo
Origin: class path resource [application.properties]:1:20
Reason: failed to convert java.lang.String to java.lang.Boolean
當(dāng)我們?yōu)閷傩耘渲缅e誤的值時,而又不希望 Spring Boot 應(yīng)用啟動失敗,我們可以設(shè)置?ignoreInvalidFields
?屬性為 true (默認(rèn)為 false)
@Data
@ConfigurationProperties(prefix = "myapp.mail",ignoreInvalidFields = true)
public class MailModuleProperties{
private Boolean enabled = Boolean.TRUE;
}
這樣,Spring Boot 將會設(shè)置 enabled 字段為我們在 Java 代碼里設(shè)定好的默認(rèn)值。如果我們沒有設(shè)置默認(rèn)值,enabled 將為 null,因為這里定義的是 boolean 的包裝類 Boolean
未知的屬性
和上面的情況有些相反,如果我們在 application.properties 文件提供了 MailModuleProperties 類不知道的屬性會發(fā)生什么?
myapp.mail.enabled=true
myapp.mail.default-subject=This is a Test
myapp.mail.unknown-property=foo
默認(rèn)情況下,Spring Boot 會忽略那些不能綁定到?@ConfigurationProperties
?類字段的屬性
然而,當(dāng)配置文件中有一個屬性實際上沒有綁定到?@ConfigurationProperties
?類時,我們可能希望啟動失敗。也許我們以前使用過這個配置屬性,但是它已經(jīng)被刪除了,這種情況我們希望被觸發(fā)告知手動從 application.properties 刪除這個屬性
為了實現(xiàn)上述情況,我們僅需要將?ignoreUnknownFields
?屬性設(shè)置為 false (默認(rèn)是 true)
@Data
@ConfigurationProperties(prefix="myapp.mail",ignoreUnknownFields=false)
class MailModuleProperties{
private Boolean enabled = Boolean.TRUE;
private String defaultSubject;
}
現(xiàn)在,應(yīng)用啟動時,控制臺會反饋我們異常信息
Binding to target [Bindable@cf65451 type = com.example.configurationproperties.properties.MailModuleProperties, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(value=myapp.mail, prefix=myapp.mail, ignoreInvalidFields=false, ignoreUnknownFields=false)]] failed:
Property: myapp.mail.unknown-property
Value: foo
Origin: class path resource [application.properties]:3:29
Reason: The elements [myapp.mail.unknown-property] were left unbound.
棄用警告??(Deprecation Warning)
ignoreUnknownFields
?在未來 Spring Boot 的版本中會被標(biāo)記為 deprecated,因為我們可能有兩個帶有?@ConfigurationProperties
?的類,同時綁定到了同一個命名空間 (namespace) 上,其中一個類可能知道某個屬性,另一個類卻不知道某個屬性,這樣就會導(dǎo)致啟動失敗
啟動時校驗 @ConfigurationProperties
如果我們希望配置參數(shù)在傳入到應(yīng)用中時有效的,我們可以通過在字段上添加?bean validation
?注解,同時在類上添加?@Validated
注解
@ConfigurationProperties(prefix = "myapp.mail")
@Validated
@Data
class MailModuleProperties{
@NotNull
private Boolean enabled;
@NotEmpty
private String defaultSubject;
}
如果我們忘記在 application.properties 文件設(shè)置 enabled 屬性,并且設(shè)置 defaultSubject 為空
myapp.mail.default-subject=
應(yīng)用啟動時,我們將會得到?BindValidationException
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'myapp.mail' to com.example.configurationproperties.properties.MailModuleProperties failed:
Property: myapp.mail.enabled
Value: null
Reason: must not be null
Property: myapp.mail.defaultSubject
Value: null
Reason: must not be empty
當(dāng)然這些默認(rèn)的驗證注解不能滿足你的驗證要求,我們也可以自定義注解
如果你的驗證邏輯很特殊,我們可以實現(xiàn)一個方法,并用 @PostConstruct 標(biāo)記,如果驗證失敗,方法拋出異常即可, 關(guān)于 @PostConstruct,可以查看?Spring Bean 的生命周期,我從哪里來?
復(fù)雜屬性類型
多數(shù)情況,我們傳遞給應(yīng)用的參數(shù)是基本的字符串或數(shù)字。但是,有時我們需要傳遞諸如 List 的數(shù)據(jù)類型
List 和 Set
假如,我們?yōu)猷]件模塊提供了一個 SMTP 服務(wù)的列表,我們可以添加該屬性到 MailModuleProperties 類中
@Data
@Component
@ConfigurationProperties(prefix="myapp.maill")
public class MailModuleProperties{
private List<String> smtpServers;
}
我們有兩種方式讓 Spring Boot 自動填充該 list 屬性
application.properties
在 application.properties 文件中以數(shù)組形式書寫
myapp.mail.smtpServers[0]=sever1
myapp.mail.smtpServers[1]=server2
application.yml
YAML 本身支持 list 類型,所以可以在 application.yml 文件中添加:
myapp:
mail:
smtp-servers:
- server1
- server2
set 集合也是這種方式的配置方式,不再重復(fù)書寫。另外YAML 是更好的閱讀方式,層次分明,所以在實際應(yīng)用中更推薦大家使用該種方式做數(shù)據(jù)配置
Duration
Spring Boot 內(nèi)置支持從配置參數(shù)中解析 durations (持續(xù)時間),官網(wǎng)文檔?給出了明確的說明
@Data
@ConfigurationProperties(prefix="myapp.mail")
class MailModuleProperties{
private Duration pauseBetweenMails;
}
我們既可以配置毫秒數(shù)數(shù)值,也可配置帶有單位的文本:
myapp.mail.pause-between-mails=5s
官網(wǎng)上已明確說明,配置 duration 不寫單位,默認(rèn)按照毫秒來指定,我們也可已通過 @DurationUnit 來指定單位:
@Data
@ConfigurationProperties(prefix = "myapp.mail")
class MailModuleProperties{
@DurationUnit(ChronoUnit.SECONDS)
pirvate Duration pauseBetweenMails;
}
常用單位如下:
-
ns
?for nanoseconds (納秒) -
us
?for microseconds (微秒) -
ms
?for milliseconds (毫秒) -
s
?for seconds (秒) -
m
?for minutes (分) -
h
?for hours (時) -
d
?for days (天)
DataSize
與 Duration 的用法一毛一樣,默認(rèn)單位是 byte (字節(jié)),可以通過 @DataSizeUnit 單位指定:
@Data
@ConfigurationProperties(prefix = "myapp.mail")
class MailModuleProperties{
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize maxAttachmentSize = DataSize.ofMegabytes(2);
}
添加配置
myapp.mail.max-attachment-size=1MB
但是,我測試的時候打印出來結(jié)果都是以 B (bytes) 來顯示
常見單位如下:
-
B
?for bytes -
KB
?for kilobytes -
MB
?for megabytes -
GB
?for gigabytes -
TB
?for terabytes
自定義類型
有些情況,我們想解析配置參數(shù)到我們自定義的對象類型上,假設(shè),我們我們設(shè)置最大包裹重量:
myapp.mail.max-attachment-weight=5kg
在 MailModuleProperties 中添加 Weight 屬性
@Data
@ConfigurationProperties(prefix="myapp.mail")
class MailModuleProperties{
private Weight maxAttachmentWeight;
}
我們可以模仿 DataSize 和 Duration 創(chuàng)造自己的 converter (轉(zhuǎn)換器)
class WeightConverter implements Converter<String,Weight>{
@Override
public Weight convert(String source){
// create and return a Weight object from the String
}
}
將其注冊到 Spring Boot 上下文中
@Configuration
class PropertiesConfig{
@Bean
@ConfigurationPropertiesBinding
public WeightConverter weightConverter(){
return new WeightConverter();
}
}
@ConfigurationPropertiesBinding
?注解是讓 Spring Boot 知道使用該轉(zhuǎn)換器做數(shù)據(jù)綁定
使用 Spring Boot Configuration Processor 完成自動補(bǔ)全
我們向項目中添加依賴:
Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Gradle
annotationProcessor 'org.springframework.boot:spring-boot-configuration-process
重新 build 項目之后,configuration processor 會為我們創(chuàng)建一個 JSON 文件:
?
這樣,當(dāng)我們在 application.properties 和 application.yml 中寫配置的時候會有自動提醒:
標(biāo)記配置屬性為 Deprecated
configuration processor 允許我們標(biāo)記某一個屬性為 deprecated
@DeprecatedConfigurationProperty(reason="change name",replacement="none")
public String getDefaultSubject(){
return defaultSubject;
}
我們可以通過添加?@DeprecatedConfigurationProperty
?注解到字段的 getter 方法上,來標(biāo)示該字段為 deprecated,重新 build 項目,看看 JSON 文件發(fā)生了什么?
當(dāng)我們再編寫配置文件時,已經(jīng)給出了明確 deprecated 提示:
總結(jié)
Spring Boot 的?@ConfigurationProperties
?注解在綁定類型安全的 Java Bean 時是非常強(qiáng)大的,我們可以配合其注解屬性和?@DeprecatedConfigurationProperty
?注解獲取到更友好的編程方式,同時這樣讓我們的配置更加模塊化。
附加說明
以為?@ConfigurationProperties
?注解滿足我們的全部需要了嗎?其實不然,Spring 官網(wǎng)明確給出了該注解和?@Value
?注解的對比:
如果使用 SpEL 表達(dá)式,我們只能選擇?@Value
?注解
另外我之前在閱讀 RabbitMQ 源碼時,發(fā)現(xiàn) RabbitProperties 類充分的利用了?@ConfigurationProperties
?注解特性:
- deprecated
- Duration
- Enum
- 嵌套屬性
感覺自己后知后覺,最近在思考,為什么小時候要閱讀和背誦古詩詞,文言文等經(jīng)典,因為這樣寫文章就可以輕松熟練的引用經(jīng)典。技術(shù)也一樣,各種框架的源碼就是學(xué)生時代的古詩詞和文言文,我們要多多查看閱讀,甚至背誦編程思想,這樣就可以寫出越來越優(yōu)雅的代碼
關(guān)于?@ConfigurationProperties
?注解的使用,這里推薦?RabbitMQ Github 源碼,只需看這一個類就可以,知道怎樣充分利用這個注解.
Demo 代碼獲取,回復(fù)公眾號「demo」,打開鏈接查看對應(yīng)的子文件夾即可
?
?
?
?
?
?
原文鏈接:https://blog.csdn.net/baidu_37366055/article/details/109990010
相關(guān)推薦
- 2023-02-12 Jupyter?notebook如何實現(xiàn)打開數(shù)據(jù)集_python
- 2022-04-01 Python?eval()?函數(shù)看這一篇就夠了_python
- 2022-09-04 C++實現(xiàn)ETW進(jìn)行進(jìn)程變動監(jiān)控詳解_C 語言
- 2022-07-06 Flutter?DateTime日期轉(zhuǎn)換的詳細(xì)使用_Android
- 2022-05-22 ansible管理工具的環(huán)境及部署安裝_服務(wù)器其它
- 2022-09-08 pytest實現(xiàn)多進(jìn)程與多線程運行超好用的插件_python
- 2023-01-29 Python?find()、rfind()方法及作用_python
- 2022-12-12 flutter?Bloc?實現(xiàn)原理示例解析_Android
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支