摘要:在項目中,為滿足以上要求,我們將大量的參數(shù)配置在或文件中,通過注解,我們可以方便的獲取這些參數(shù)值使用配置模塊假設(shè)我們正在搭建一個發(fā)送郵件的模塊。這使得在不影響其他模塊的情況下重構(gòu)一個模塊中的屬性變得容易。
在編寫項目代碼時,我們要求更靈活的配置,更好的模塊化整合。在 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ù):
我們可以使用 @Value 注解或著使用 Spring Environment bean 訪問這些屬性,是這種注入配置方式有時顯得很笨重。我們將使用更安全的方式(@ConfigurationProperties )來獲取這些屬性
@ConfigurationProperties 的基本用法非常簡單:我們?yōu)槊總€要捕獲的外部屬性提供一個帶有字段的類。請注意以下幾點:
前綴定義了哪些外部屬性將綁定到類的字段上
根據(jù) Spring Boot 寬松的綁定規(guī)則,類的屬性名稱必須與外部屬性的名稱匹配
我們可以簡單地用一個值初始化一個字段來定義一個默認(rèn)值
類本身可以是包私有的
類的字段必須有公共 setter 方法
Spring 寬松綁定規(guī)則 (relaxed binding)Spring使用一些寬松的綁定屬性規(guī)則。因此,以下變體都將綁定到 hostName 屬性上:
如果我們將 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 掃描到
很顯然,只有當(dāng)類所在的包被 Spring @ComponentScan 注解掃描到才會生效,默認(rèn)情況下,該注解會掃描在主應(yīng)用類下的所有包結(jié)構(gòu)
我們也可以通過 Spring 的 Java Configuration 特性實現(xiàn)同樣的效果:
只要 MailModuleConfiguration 類被 Spring Boot 應(yīng)用掃描到,我們就可以在應(yīng)用上下文中訪問 MailModuleProperties bean
我們還可以使用 @EnableConfigurationProperties 注解讓我們的類被 Spring Boot 所知道,在該注解中其實是用了@Import(EnableConfigurationPropertiesImportSelector.class) 實現(xiàn),大家可以看一下
激活一個 @ConfigurationProperties 類的最佳方式是什么?無法轉(zhuǎn)換的屬性所有上述方法都同樣有效。然而,我建議模塊化你的應(yīng)用程序,并讓每個模塊提供自己的@ConfigurationProperties 類,只提供它需要的屬性,就像我們在上面的代碼中對郵件模塊所做的那樣。這使得在不影響其他模塊的情況下重構(gòu)一個模塊中的屬性變得容易。
因此,我不建議在應(yīng)用程序類本身上使用 @EnableConfigurationProperties,如許多其他教程中所示,是在特定于模塊的 @Configuration 類上使用@EnableConfigurationProperties,該類也可以利用包私有的可見性對應(yīng)用程序的其余部分隱藏屬性。
如果我們在 application.properties 屬性上定義的屬性不能被正確的解析會發(fā)生什么?假如我們?yōu)樵緫?yīng)該為布爾值的屬性提供的值為 "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)
這樣,Spring Boot 將會設(shè)置 enabled 字段為我們在 Java 代碼里設(shè)定好的默認(rèn)值。如果我們沒有設(shè)置默認(rèn)值,enabled 將為 null,因為這里定義的是 boolean 的包裝類 Boolean
未知的屬性和上面的情況有些相反,如果我們在 application.properties 文件提供了 MailModuleProperties 類不知道的屬性會發(fā)生什么?
默認(rèn)情況下,Spring Boot 會忽略那些不能綁定到 @ConfigurationProperties 類字段的屬性
然而,當(dāng)配置文件中有一個屬性實際上沒有綁定到 @ConfigurationProperties 類時,我們可能希望啟動失敗。也許我們以前使用過這個配置屬性,但是它已經(jīng)被刪除了,這種情況我們希望被觸發(fā)告知手動從 application.properties 刪除這個屬性
為了實現(xiàn)上述情況,我們僅需要將 ignoreUnknownFields 屬性設(shè)置為 false (默認(rèn)是 true)
現(xiàn)在,應(yīng)用啟動時,控制臺會反饋我們異常信息
Binding to target [Bindable@cf65451 type = com.example.configurationproperties.properties.MailModuleProperties, value = "provided", annotations = array[@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)啟動時校驗 @ConfigurationProperties
ignoreUnknownFields 在未來 Spring Boot 的版本中會被標(biāo)記為 deprecated,因為我們可能有兩個帶有 @ConfigurationProperties 的類,同時綁定到了同一個命名空間 (namespace) 上,其中一個類可能知道某個屬性,另一個類卻不知道某個屬性,這樣就會導(dǎo)致啟動失敗
如果我們希望配置參數(shù)在傳入到應(yīng)用中時有效的,我們可以通過在字段上添加 bean validation 注解,同時在類上添加 @Validated 注解
如果我們忘記在 application.properties 文件設(shè)置 enabled 屬性,并且設(shè)置 defaultSubject 為空
應(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 類中
我們有兩種方式讓 Spring Boot 自動填充該 list 屬性
application.properties在 application.properties 文件中以數(shù)組形式書寫
YAML 本身支持 list 類型,所以可以在 application.yml 文件中添加:
set 集合也是這種方式的配置方式,不再重復(fù)書寫。另外YAML 是更好的閱讀方式,層次分明,所以在實際應(yīng)用中更推薦大家使用該種方式做數(shù)據(jù)配置
DurationSpring Boot 內(nèi)置支持從配置參數(shù)中解析 durations (持續(xù)時間),官網(wǎng)文檔 給出了明確的說明
我們既可以配置毫秒數(shù)數(shù)值,也可配置帶有單位的文本:
官網(wǎng)上已明確說明,配置 duration 不寫單位,默認(rèn)按照毫秒來指定,我們也可已通過 @DurationUnit 來指定單位:
常用單位如下:
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 單位指定:
添加配置
但是,我測試的時候打印出來結(jié)果都是以 B (bytes) 來顯示
常見單位如下:
B for bytes
KB for kilobytes
MB for megabytes
GB for gigabytes
TB for terabytes
自定義類型有些情況,我們想解析配置參數(shù)到我們自定義的對象類型上,假設(shè),我們我們設(shè)置最大包裹重量:
在 MailModuleProperties 中添加 Weight 屬性
我們可以模仿 DataSize 和 Duration 創(chuàng)造自己的 converter (轉(zhuǎn)換器)
將其注冊到 Spring Boot 上下文中
@ConfigurationPropertiesBinding 注解是讓 Spring Boot 知道使用該轉(zhuǎn)換器做數(shù)據(jù)綁定
使用 Spring Boot Configuration Processor 完成自動補(bǔ)全我們向項目中添加依賴:
Maven Gradle重新 build 項目之后,configuration processor 會為我們創(chuàng)建一個 JSON 文件:
這樣,當(dāng)我們在 application.properties 和 application.yml 中寫配置的時候會有自動提醒:
configuration processor 允許我們標(biāo)記某一個屬性為 deprecated
我們可以通過添加 @DeprecatedConfigurationProperty 注解到字段的 getter 方法上,來標(biāo)示該字段為 deprecated,重新 build 項目,看看 JSON 文件發(fā)生了什么?
當(dāng)我們再編寫配置文件時,已經(jīng)給出了明確 deprecated 提示:
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)的子文件夾即可
靈魂追問在實際項目中, 你能夠充分利用這些特性讓你的配置更靈活和模塊化嗎?
閱讀框架源碼時,他們都是怎樣配置的呢?
@Value 注解怎樣給出默認(rèn)值?
提高效率工具[center]
推薦閱讀紅黑樹,超強(qiáng)動靜圖詳解,簡單易懂
只會用 git pull ?有時候你可以嘗試更優(yōu)雅的處理方式
雙親委派模型:大廠高頻面試題,輕松搞定
面試還不知道BeanFactory和ApplicationContext的區(qū)別?
如何設(shè)計好的RESTful API
歡迎持續(xù)關(guān)注公眾號:「日拱一兵」前沿 Java 技術(shù)干貨分享
高效工具匯總
面試問題分析與解答
技術(shù)資料領(lǐng)取
以讀偵探小說思維輕松趣味學(xué)習(xí) Java 技術(shù)棧相關(guān)知識,本著將復(fù)雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術(shù)問題,技術(shù)持續(xù)更新,請持續(xù)關(guān)注......
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/77914.html
摘要:在項目中,為滿足以上要求,我們將大量的參數(shù)配置在或文件中,通過注解,我們可以方便的獲取這些參數(shù)值使用配置模塊假設(shè)我們正在搭建一個發(fā)送郵件的模塊。這使得在不影響其他模塊的情況下重構(gòu)一個模塊中的屬性變得容易。 在編寫項目代碼時,我們要求更靈活的配置,更好的模塊化整合。在 Spring Boot 項目中,為滿足以上要求,我們將大量的參數(shù)配置在 application.properties 或...
摘要:也是自帶的一個基于線程池設(shè)計的定時任務(wù)類。其每個調(diào)度任務(wù)都會分配到線程池中的一個線程執(zhí)行,所以其任務(wù)是并發(fā)執(zhí)行的,互不影響。 原創(chuàng)不易,如需轉(zhuǎn)載,請注明出處https://www.cnblogs.com/baixianlong/p/10659045.html,否則將追究法律責(zé)任?。?! 一、在JAVA開發(fā)領(lǐng)域,目前可以通過以下幾種方式進(jìn)行定時任務(wù) 1、單機(jī)部署模式 Timer:jdk中...
摘要:豐富的特性還支持通知過期等等特性。到這個就說明測試通過了。主要針對方法配置,能夠根據(jù)方法的請求參數(shù)對其進(jìn)行緩存,常用于查詢操作主要針對方法配置,能夠根據(jù)方法的請求參數(shù)對其進(jìn)行緩存,常用于修改操作清空緩存,主要用于刪除操作。 [TOC] Redis簡介 Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協(xié)議、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value 數(shù)...
摘要:動態(tài)地代理,可以猜測一下它的含義,在運行時動態(tài)地對某些東西代理,代理它做了其他事情。所以動態(tài)代理的內(nèi)容重點就是這個。所以下一篇我們來細(xì)致了解下的到底是怎么使用動態(tài)代理的。 之前講了《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》,本來打算下一篇講講Srping的AOP的,但是其中會涉及到Java的動態(tài)代理,所以先單獨一篇來了解下Java的動態(tài)代理到底是什么,Java是怎么實現(xiàn)它的。 ...
摘要:前言上一次我們對的應(yīng)用進(jìn)行了一次全面的分析,這一次我們來聊聊。 showImg(https://segmentfault.com/img/remote/1460000020077803?w=1280&h=853); 前言 上一次我們對Paging的應(yīng)用進(jìn)行了一次全面的分析,這一次我們來聊聊WorkManager。 如果你對Paging還未了解,推薦閱讀這篇文章: Paging在Recy...
閱讀 1038·2021-11-18 13:23
閱讀 745·2021-11-08 13:16
閱讀 854·2021-10-11 10:58
閱讀 3509·2021-09-22 15:26
閱讀 1730·2021-09-08 10:42
閱讀 1806·2021-09-04 16:45
閱讀 1732·2019-08-30 15:54
閱讀 2563·2019-08-30 13:45