springboot自定義yml配置文件及其外部部署過(guò)程
1、序
背景:有個(gè)小項(xiàng)目需要后臺(tái),俺頂著Java菜逼的頭銜接下來(lái)了,被男票瘋狂安利spring boot,于是(被逼無(wú)奈)開(kāi)始了邊學(xué)邊開(kāi)發(fā)的躺坑之路……真香,資料超多,超好用!??!
電廠(chǎng)的項(xiàng)目,用了公司自己開(kāi)發(fā)的實(shí)時(shí)數(shù)據(jù)庫(kù),后臺(tái)這邊就涉及到很多測(cè)點(diǎn)的信息需要存儲(chǔ)到配置文件(為什么不是關(guān)系數(shù)據(jù)庫(kù)真的不要問(wèn)我),并且希望在部署的時(shí)候方便修改,考慮到內(nèi)容頗多,放在application-pro.yml中實(shí)在不合適,就加了個(gè)point.yml。倒不是因?yàn)楝F(xiàn)場(chǎng)測(cè)點(diǎn)信息會(huì)變才需要更改,更多的是突然一拍腦袋,發(fā)現(xiàn)手抖寫(xiě)錯(cuò)了?
首先,因?yàn)橐徊恍⌒淖兂闪藊xx.yml玩家,好好用哦,沒(méi)能回去xxx.properties,傳說(shuō)中官方不支持像加載xxx.properties配置文件那樣使用注解@PropertySource("classpath:xxx.properties")的方式加載yml配置文件,這里要說(shuō)的就是加載自定義yml文件的方法。
官方說(shuō)明看一下
加載自定義xxx.properties文件的方法參考這篇文章:
注意:之前在找多數(shù)據(jù)源配置的資料時(shí),就因?yàn)橘Y料對(duì)應(yīng)的spring boot版本差異搞得很郁悶,請(qǐng)務(wù)必注意俺用的版本是:
spring boot 2.13
2、加載自定義yml文件
spring boot的資料非常多,多到非常容易不用動(dòng)腦就解決了問(wèn)題呢~項(xiàng)目做完之后冷靜下來(lái),覺(jué)得還是應(yīng)該驗(yàn)證一下,畢竟打臉是為了以后有頭有臉。
2.1、使用@PropertiesSource注解讀取yml配置文件-簡(jiǎn)單版
按照上面給出的官宣,這條路是不行的。因?yàn)闆](méi)看到文檔對(duì)應(yīng)的版本號(hào),還是試一下:
# 配置文件 point.yml id: 2233 name: Ellie
(呃,這種信息為啥要叫point啊啊?。?/p>
// 配置對(duì)應(yīng)的config類(lèi) @Data @Configuration @PropertySource(value = {"classpath:point.yml"}) @ConfigurationProperties() public class TestPoint { private int id; private String name; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
隨手糊了個(gè)controller來(lái)測(cè)試
@RestController public class TestConfigController { @Resource TestPoint testPoint; @ApiOperation("測(cè)試 配置文件") @RequestMapping(value = "/config") public ResultBean<String> testConfig() { return ResultBeanUtil.makeOkResp(testPoint.toString()); } }
postman搞起來(lái)
都挺好!
所以如果只是要讀取這樣簡(jiǎn)單的信息的話(huà),直接使用注解@PropertiesSource是可以的,官方說(shuō)的不確定的影響我也不知道是啥哦。
2.2、使用@PropertiesSource注解讀取yml配置文件-不簡(jiǎn)單版?
加個(gè)list<基礎(chǔ)類(lèi)型>看看。
# point.yml id: 2233 name: Ellie cards: - XD02101263 - ZY8965 - GX0009 // 配置類(lèi) @Data @Configuration @PropertySource(value = {"classpath:point.yml"}) @ConfigurationProperties() public class TestPoint { private int id; private String name; private List<String> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } }
postman
裝逼失敗,不行了哦。使用@Value("id")注解也是不行的呢,因?yàn)檫@個(gè)注解是用來(lái)匹配變量名稱(chēng)和配置文件不一致的情況。
按照其他博客里講的(才糊代碼一個(gè)月根本沒(méi)有深入看原理的我只好是:大佬說(shuō)啥就是啥),是因?yàn)槭褂聾PropertySource注解只能加載yml配置文件,但不能將其配置信息暴露給spring environment,需要手動(dòng)暴露。方法就是在讓application啟動(dòng)的時(shí)候把下面的bean加載。
@Bean public static PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); yaml.setResources(new ClassPathResource("point.yml")); configurer.setProperties(yaml.getObject()); return configurer; }
偷懶的我直接丟到了main函數(shù)所在的.java文件。運(yùn)行:
真的不是我截錯(cuò)圖哦。
2.3、加前綴可行版
畢竟我這么機(jī)智(無(wú)腦分析?。?,悄咪咪加了個(gè)前綴,前綴的名字隨意取哈,與配置類(lèi)中對(duì)應(yīng)即可,我只是偷懶叫做prefix。
# point.yml prefix: id: 2233 name: Ellie cards: - XD02101263 - ZY8965 - GX0009 // config類(lèi) @Data @Configuration @PropertySource(value = {"classpath:point.yml"}) @ConfigurationProperties(prefix = "prefix") public class TestPoint { private int id; private String name; private List<String> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } }
都挺好大結(jié)局?
求助:
這樣為什么可行,俺是一點(diǎn)都不曉得的,如果有大佬路過(guò),請(qǐng)幫忙解答?。?!跪謝orz
順便說(shuō)一句,出于好奇,試了下某些博文里說(shuō)的前綴加yml分隔符---配合的方式,感覺(jué)上是一本正經(jīng)胡說(shuō)八道,實(shí)際上也沒(méi)讀出來(lái)。讀取List<類(lèi)>也是同樣可行的。
# point.yml prefix: id: 2233 name: Ellie cards: - name: XD code: XD02101263 - name: ZY code: ZY8965 - name: GX code: GX0009 // config 類(lèi) @Data @Configuration @PropertySource(value = {"classpath:point.yml"}) @ConfigurationProperties(prefix = "prefix") public class TestPoint { private int id; private String name; private List<Card> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } } // 并不需要有什么與眾不同的card類(lèi) @Data public class Card { private String name; private String code; @Override public String toString() { return "Card{" + "name='" + name + '\'' + ", code='" + code + '\'' + '}'; } }
請(qǐng)求
查找資料的過(guò)程中還看到了一種別致的寫(xiě)法,是為了解決多層嵌套的yml的讀寫(xiě),未驗(yàn)證,因?yàn)橛羞x擇的話(huà),我不愿意這樣寫(xiě),不過(guò)寫(xiě)法確實(shí)很別致,哈哈哈!http://www.dbjr.com.cn/article/242026.htm
3、外部部署
其實(shí)就是把配置文件部署在jar包外部,方便修改而不必重新打包。
3.1、spring boot核心配置文件外部加載
希望外部加載自定義配置文件,需要先了解spring默認(rèn)的文件加載方式。
spring程序會(huì)按優(yōu)先級(jí)從下面這些路徑來(lái)加載application.properties配置文件:
- 當(dāng)前目錄下的/config目錄
- 當(dāng)前目錄
- classpath里的/config目錄
- classpath 根目錄
idea中,在源碼下的classpath對(duì)應(yīng)src/main/resources很明確,打包后的classpath在哪里俺是不知道的,然后就把打包后的jar包解壓看了下,在BOOT-INF\classes下看到了application.yml和point.yml。所以要想覆蓋配置文件,我再jar包同級(jí)目錄下建了config文件夾,修改配置文件內(nèi)容看覆蓋是否生效。
具體操作:
- 打包的時(shí)候默認(rèn)將application.yml和point.yml打包到j(luò)ar中(classpath)
- 部署時(shí),jar包同級(jí)目錄下建立config文件夾,修改application.yml中端口號(hào)和point.yml內(nèi)容,看修改是否生效。
修改后的point.yml文件如下:
prefix: id: 2233 name: FakeEllie cards: - name: NONE code: 00000001
測(cè)試結(jié)果:端口號(hào)修改生效(application.yml修改生效),修改后的point.yml并未生效。
畢竟自定義配置文件,一廂情愿希望spring boot按照核心文件加載方式加載point.yml,沒(méi)有生效也在意料之中,不過(guò)路并沒(méi)有堵死。
3.2、在@PropertySource中添加路徑
查資料的時(shí)候注意到還有這種寫(xiě)法:
@Data @Configuration @PropertySource(value = {"file:config/point.yml"}) @ConfigurationProperties(prefix = "prefix") public class TestPoint { private int id; private String name; private List<Card> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } }
就是通過(guò)file來(lái)指定文件路徑,之前是classpath來(lái)指定資源相對(duì)路徑,說(shuō)來(lái)神奇,這種方式?jīng)]有報(bào)錯(cuò),但讀取的內(nèi)容卻是classpath下的point.yml,而不是config下的point.yml。
想來(lái)是通過(guò)@ConfigurationProperties(prefix = "prefix")指定的前綴去classpath下匹配到的。跟@PropertySource(value = {"file:config/point.yml"})大概是沒(méi)有關(guān)系了,忘崽牛奶真好喝。
3.3、通過(guò)YamlPropertiesFactoryBean添加路徑
回想上面的描述,YamlPropertiesFactoryBean是將配置文件暴露給spring環(huán)境的,可以考慮使用它來(lái)指定文件路徑。
修改bean,添加new FileSystemResource("config/point.yml")來(lái)指定config文件夾下的配置。
@Bean public static PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); yaml.setResources(new ClassPathResource("point.yml"), new FileSystemResource("config/point.yml")); configurer.setProperties(yaml.getObject()); return configurer; }
此時(shí)配置類(lèi)上使用@PropertySource(value = {"file:config/point.yml"})這種寫(xiě)法,返回的是
成功了?但是好像搞笑了。不過(guò)也說(shuō)明了配置文件讀取的順序。config文件夾下的有最終決定權(quán)。
為了直觀(guān)些,俺順手修改jar包同級(jí)目錄下config文件夾中point.yml配置文件,保證list元素個(gè)數(shù)相同:
prefix: id: 2233 name: FakeEllie cards: - name: NONE code: 00000001 - name: NONE code: 00000002 - name: NONE code: 00000003
不搞笑了。
但是,改成此時(shí)配置類(lèi)上使用@PropertySource(value = {"classpath:point.yml"})后,返回并沒(méi)有變化。所以YamlPropertiesFactoryBean將配置文件暴露給spring環(huán)境,說(shuō)的應(yīng)該就是將文件添加到spring的classpath下了,先讀默認(rèn)的,再讀新添加這樣子的。
然鵝這樣就沒(méi)有辦法在不進(jìn)行外部配置的時(shí)候使用默認(rèn)的classpath下的配置文件了。
此外,通過(guò)YamlPropertiesFactoryBean添加配置文件的方式,就需要保證config/point.yml一定要存在,要想達(dá)到不進(jìn)行外部配置的時(shí)候讀取默認(rèn)classpath下point.yml,在進(jìn)行外部配置的時(shí)候讀取config/point.yml。那就只好耍流氓了。
@Data @Configuration @PropertySource(value = {"file:config/point.yml", "classpath:point.yml"}, ignoreResourceNotFound = true) @ConfigurationProperties(prefix = "prefix") public class TestPoint { private int id; private String name; private List<Card> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } }
重點(diǎn):然后!在不進(jìn)行外部配置的時(shí)候,config/point.yml內(nèi)容為空,或者干脆跟classpath下的point.yml內(nèi)容保持一致。
小孩子才做選擇題,我全都想要
雖然看上去像個(gè)意外,但是好在意啊啊啊啊,遏制不住的好奇心?。【褪莿倓偰莻€(gè)拼起來(lái)的返回值。
想看看是不是application.yml覆蓋list也會(huì)這樣,俺把配置類(lèi)對(duì)應(yīng)的內(nèi)容舉家搬遷到了application.yml中。如下:
// 配置類(lèi) @Data @Configuration @ConfigurationProperties(prefix = "prefix") public class TestPoint { private int id; private String name; private List<Card> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } }
配置類(lèi)默認(rèn)讀取application.yml。
# classpath:application.yml prefix: id: 2233 name: Ellie cards: - name: XD code: XD02101263 - name: ZY code: ZY8965 - name: GX code: GX0009 #config/application.yml prefix: id: 2233 name: FakeEllie cards: - name: NONE code: 00000001
測(cè)試結(jié)果:
并沒(méi)有進(jìn)行拼接啊喂?。?!
在各種調(diào)換順序看影響的時(shí)候,修改了YamlPropertiesFactoryBean添加source的順序,返回結(jié)果發(fā)生了變化。
@Bean public static PropertySourcesPlaceholderConfigurer properties() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); yaml.setResources(new FileSystemResource("config/point.yml"), new ClassPathResource("point.yml")); configurer.setProperties(yaml.getObject()); configurer.setIgnoreResourceNotFound(true); return configurer; }
返回結(jié)果
有一種簡(jiǎn)單的在運(yùn)行時(shí)通過(guò)命令參數(shù)指定配置文件的方式效果與此類(lèi)似。
java -jar demo.jar --Dspring.config.location=point.yml
俺的原則時(shí),代碼能解決的,就不要交給人來(lái)解決。
雖然沒(méi)有解決任何問(wèn)題,但是順便知道了讀取的先后順序就是setResources的先后順序。卒
所以目前的結(jié)論是,對(duì)于有l(wèi)ist的配置,并且個(gè)數(shù)發(fā)生變化的時(shí)候,這種方式并不適用。
3.4、自定義yaml文件資源加載類(lèi)
在注解@PropertySource中,有個(gè)屬性factory主要用來(lái)聲明解析配置文件的類(lèi),這個(gè)類(lèi)必須是PropertySourceFactory接口的實(shí)現(xiàn)。從這里入手。
參考資料:
Spring Boot自定義加載yml實(shí)現(xiàn),附源碼解讀
默認(rèn)調(diào)用的是PropertySourceFactory的實(shí)現(xiàn)DefaultPropertySourceFactory,因此可以自定義factory實(shí)現(xiàn)PropertySourceFactory接口,也可以擴(kuò)展DefaultPropertySourceFactory類(lèi)。兩種寫(xiě)法的效果是一樣的,列出來(lái)。
直接實(shí)現(xiàn)PropertySourceFactory接口
public class YamlPropertyLoaderFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException { List<PropertySource<?>> sources = name != null ? new YamlPropertySourceLoader().load(name, encodedResource.getResource()) : new YamlPropertySourceLoader().load( getNameForResource(encodedResource.getResource()), encodedResource.getResource()); if (sources.size() == 0) { return null; } return sources.get(0); } private static String getNameForResource(Resource resource) { String name = resource.getDescription(); if (!StringUtils.hasText(name)) { name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource); } return name; } }
擴(kuò)展DefaultPropertySourceFactory
public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { if (resource == null) { return super.createPropertySource(name, resource); } List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()); if (sources.size() == 0) { return super.createPropertySource(name, resource); } return sources.get(0); } }
建議用第二種方式。
用factory的方式來(lái)實(shí)現(xiàn)的話(huà),前面莫名其妙加個(gè)prefix就可以正常讀取的詭異操作也不需要了哦。
使用方式如下:
@Data @Configuration @PropertySource(value = {"classpath:point.yml", "file:config/point.yml"}, factory = YamlPropertyLoaderFactory.class, ignoreResourceNotFound = true) @ConfigurationProperties public class TestPoint{ private int id; private String name; private List<Card> cards; @Override public String toString() { return "TestPoint{" + "id=" + id + ", name='" + name + '\'' + ", cards=" + cards + '}'; } } # config/point.yml id: 2233 name: FakeEllie cards: - name: NONE code: 00000001
測(cè)試結(jié)果:
自定義factory的方式,讀取多種路徑的配置文件時(shí),也是有先后順序的,就是@PropertySource中value屬性指定的順序,與使用YamlPropertiesFactoryBean將資源暴露給spring環(huán)境不同,這個(gè)不會(huì)有前面出現(xiàn)的“拼接”效果出現(xiàn),棒呆~(yú)
以解決問(wèn)題為目標(biāo)和以寫(xiě)清楚文章為目標(biāo)去看同樣的問(wèn)題,真的是不一樣的探索路徑呢,湊字?jǐn)?shù)和為了flag不倒的文寫(xiě)的遠(yuǎn)遠(yuǎn)超出自己最初的預(yù)期,真好,超喜歡!
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot中的yml文件中讀取自定義配置信息及遇到問(wèn)題小結(jié)
- Springboot關(guān)于自定義stater的yml無(wú)法提示問(wèn)題解決方案
- springboot如何通過(guò)@PropertySource加載自定義yml文件
- SpringBoot自定義加載yml實(shí)現(xiàn)方式,附源碼解讀
- springboot如何獲取yml文件的自定義參數(shù)
- Springboot如何獲取配置文件application.yml中自定義的變量并使用
- 在SpringBoot 中從application.yml中獲取自定義常量方式
- springboot解析自定義yml方式
相關(guān)文章
Java整合Redis實(shí)現(xiàn)坐標(biāo)附近查詢(xún)功能
這篇文章主要介紹了Java整合Redis實(shí)現(xiàn)坐標(biāo)附近查詢(xún),我們可以在redis服務(wù)器使用命令 help xxx 查看指令的具體用法,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2023-11-11深入Spring Boot實(shí)現(xiàn)對(duì)Fat Jar jsp的支持
這篇文章主要介紹了深入Spring Boot實(shí)現(xiàn)對(duì)Fat Jar jsp的支持,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06基于mybatis注解動(dòng)態(tài)sql中foreach工具的方法
這篇文章主要介紹了mybatis注解動(dòng)態(tài)sql中foreach工具方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11java并發(fā)編程工具類(lèi)PriorityBlockingQueue優(yōu)先級(jí)隊(duì)列
這篇文章主要為大家介紹了java并發(fā)編程工具類(lèi)PriorityBlockingQueue優(yōu)先級(jí)隊(duì)列的方法示例應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03使用Springboot整合GridFS實(shí)現(xiàn)文件操作
這篇文章主要介紹了使用Springboot整合GridFS實(shí)現(xiàn)文件操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java并發(fā)編程示例(七):守護(hù)線(xiàn)程的創(chuàng)建和運(yùn)行
這篇文章主要介紹了Java并發(fā)編程示例(七):守護(hù)線(xiàn)程的創(chuàng)建和運(yùn)行,在本節(jié)示例中,我們將創(chuàng)建兩個(gè)線(xiàn)程,一個(gè)是普通線(xiàn)程,向隊(duì)列中寫(xiě)入事件,另外一個(gè)是守護(hù)線(xiàn)程,清除隊(duì)列中的事件,需要的朋友可以參考下2014-12-12Struts 2中實(shí)現(xiàn)Ajax的三種方式
這篇文章主要介紹了Struts 2中實(shí)現(xiàn)Ajax的三種方式,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05