SpringBoot注入自定義的配置文件的方法詳解
一、簡介
在實際的項目開發(fā)過程中,我們經(jīng)常需要將某些變量從代碼里面抽離出來,放在配置文件里面,以便更加統(tǒng)一、靈活的管理服務配置信息。比如,數(shù)據(jù)庫、eureka、zookeeper、redis、mq、kafka 等服務組件的連接參數(shù)配置,還有我們自定義的項目參數(shù)配置變量。
當然,實際上根據(jù)當前的業(yè)務需求,我們往往會自定義參數(shù),然后注入到代碼里面去,以便靈活使用!
今天,我們就一起來聊一聊SpringBoot加載配置文件的幾種玩法,如果有遺漏,歡迎留言!
SpringBoot 項目在啟用時,首先會默認加載bootstrap.properties或者bootstrap.yml這兩個配置文件(這兩個優(yōu)先級最高);接著會加載application.properties或application.yml;如果何配置了spring.profiles這個變量,同時還會加載對應的application-{profile}.properties或者application-{profile}.yml文件,profile為對應的環(huán)境變量,比如dev,如果沒有配置,則會加載profile=default的配置文件。
雖然說配置項都寫在同一個配置文件沒有問題,但是很多時候我們?nèi)匀幌M芊珠_寫,這樣會比較清晰,比如zookeeper的配置寫在zookeeper.properties,數(shù)據(jù)庫相關(guān)的配置寫在datasource.properties等等,因此就需要設置加載外部配置文件!
具體該如何實現(xiàn)呢,我們一起來看看!
二、代碼實踐
2.1 通過@value注解實現(xiàn)參數(shù)加載
當我們想要在某個類里面注入某個變量,通過@value注解就可以簡單實現(xiàn)參數(shù)的注入!
例如application.properties文件里,配置一個config.name的變量key,值為zhangsan
//參數(shù)定義 config.name=zhangsan
然后在對應的類里面,通過參數(shù)@value注入即可!
@RestController
public?class?HelloController?{
????
????@Value("${config.name}")
????private?String?config;
????@GetMapping("config")
????public?String?config(){
????????return?JSON.toJSONString(config);
????}
}
使用@value注解注入配置,通常情況下有個要求就是,注解里面的變量,必須在application.properties文件里面事先定義好,否則啟動報錯!
當然,如果我們不想讓它抱錯,我們可以給它一個缺省值xxx,比如:
@Value("${config.name:xxx}")
private?String?config;
這樣,SpringBoot 項目在啟用時不會報錯!
2.2 通過@ConfigurationProperties注解實現(xiàn)參數(shù)加載
某些場景下,@value注解并不能滿足我們所有的需求,比如參數(shù)配置的數(shù)據(jù)類型是一個對象或者數(shù)組,這個時候才用@ConfigurationProperties會是一個比較好的選擇!
配置一個對象類型的參數(shù)
例如在application.properties文件里,當我們想配置一個對象類型的參數(shù),我們可以這樣操作!
//參數(shù)定義 config2.name=demo_1 config2.value=demo_value_1
然后,創(chuàng)建一個配置類Config2,用于將定義的變量映射到配置類里面。
@Component
@ConfigurationProperties(prefix?=?"config2")
public?class?Config2?{
????public?String?name;
????public?String?value;
????//...get、set
}
讀取數(shù)據(jù)的方式,也很簡單,直接注入到對應的類里面就可以了
@RestController
public?class?HelloController?{
????
????@Autowired
????private?Config2?config2;
????@GetMapping("config2")
????public?String?config2(){
????????return?JSON.toJSONString(config2);
????}
}
配置一個Map類型的參數(shù)
例如在application.properties文件里,當我們想配置一個 Map 類型的參數(shù),我們可以這樣操作!
//參數(shù)定義 config3.map1.name=demo_id_1_name config3.map1.value=demo_id_1_value config3.map2.name=demo_id_2_name config3.map2.value=demo_id_2_value
然后,創(chuàng)建一個配置類Config3,用于將定義的變量映射到配置類里面。
@Component
@ConfigurationProperties(prefix?=?"config3")
public?class?Config3?{
????private?Map<String,?String>?map1?=?new?HashMap<>();
????private?Map<String,?String>?map2?=?new?HashMap<>();
????//...get、set
}
讀取數(shù)據(jù)的方式,與之類似!
@RestController
public?class?HelloController?{
????
????@Autowired
????private?Config3?config3;
????@GetMapping("config3")
????public?String?config3(){
????????return?JSON.toJSONString(config3);
????}
}
配置一個List類型的參數(shù)
例如在application.properties文件里,當我們想配置一個 List 類型的參數(shù),我們可以這樣操作!
//參數(shù)定義 config4.userList[0].enable=maillist_1_enable config4.userList[0].name=maillist_1_name config4.userList[0].value=maillist_1_value config4.userList[1].enable=maillist_2_enable config4.userList[1].name=maillist_2_name config4.userList[1].value=maillist_2_value config4.userList[2].enable=maillist_3_enable config4.userList[2].name=maillist_3_name config4.userList[2].value=maillist_3_value
然后,創(chuàng)建一個配置類Config4,用于將定義的變量映射到配置類里面。
@Component
@ConfigurationProperties(prefix?=?"config4")
public?class?Config4?{
????private?List<UserEntity>?userList;
????public?List<UserEntity>?getUserList()?{
????????return?userList;
????}
????public?void?setUserList(List<UserEntity>?userList)?{
????????this.userList?=?userList;
????}
}
public?class?UserEntity?{
????private?String?enable;
????private?String?name;
????private?String?value;
????//...get、set
}
讀取數(shù)據(jù)的方式,與之類似!
@RestController
public?class?HelloController?{
????
????@Autowired
????private?Config4?config4;
????@GetMapping("config4")
????public?String?config4(){
????????return?JSON.toJSONString(config4);
????}
}
2.3 通過@PropertySource注解實現(xiàn)配置文件加載
正如我們最開始所介紹的,很多時間,我們希望將配置文件分卡寫,比如zookeeper組件對應的服務配置文件是zookeeper.properties,redis組件對應的服務配置文件是redis.properties等等。
這種自定義的配置文件,我們應該如何加載到Spring容器里面呢?
其實方法也很簡單,通過@PropertySource就可以實現(xiàn)!
首先,我們在resources資源文件夾下,創(chuàng)建兩個配置文件test.properties和bussiness.properties,內(nèi)容如下!
test.properties文件內(nèi)容:
aaa.a1=aa1123 aaa.a2=aa2123 aaa.a3=aa3123 aaa.a4=aa4123
bussiness.properties文件內(nèi)容:
bbbb.a1=bb1123 bbbb.a2=bb2123 bbbb.a3=bb3123 bbbb.a4=bb4123
在SpringBoot啟動類上加載配置文件即可!
@SpringBootApplication
@PropertySource(value?=?{"test.properties","bussiness.properties"})
public?class?PropertyApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(PropertyApplication.class,?args);
????}
}
讀取數(shù)據(jù)的方式,與之類似!
@RestController
public?class?HelloController?{
????
????@Value("${aaa.a2}")
????private?String?a2;
????@Value("${bbbb.a1}")
????private?String?bbbbA1;
????
????@GetMapping("a2")
????public?String?a2(){
????????return?JSON.toJSONString(a2);
????}
????@GetMapping("bbbbA1")
????public?String?bbbbA1(){
????????return?JSON.toJSONString(bbbbA1);
????}
}
如果我們只是在業(yè)務中需要用到自定義配置文件的值,這樣引入并沒有什么問題;但是如果某些自定義的變量,在項目啟動的時候需要用到,這種方式會存在一些問題,原因如下:
翻譯過來的意思就是說:
雖然在@SpringBootApplication上使用@PropertySource似乎是在環(huán)境中加載自定義資源的一種方便而簡單的方法,但我們不推薦使用它,因為SpringBoot在刷新應用程序上下文之前就準備好了環(huán)境。使用@PropertySource定義的任何鍵都加載得太晚,無法對自動配置產(chǎn)生任何影響。
因此,如果某些參數(shù)是啟動項變量,建議將其定義在application.properties或application.yml文件里面,這樣就不會有問題!
或者,采用【自定義環(huán)境處理類】來實現(xiàn)配置文件的加載!
2.4 通過自定義環(huán)境處理類,實現(xiàn)配置文件的加載
實現(xiàn)方法也很簡單,首先,創(chuàng)建一個實現(xiàn)自EnvironmentPostProcessor接口的類,然后自行加載配置文件。
public?class?MyEnvironmentPostProcessor?implements?EnvironmentPostProcessor?{
????@Override
????public?void?postProcessEnvironment(ConfigurableEnvironment?environment,?SpringApplication?application)?{
????????//自定義配置文件
????????String[]?profiles?=?{
????????????????"test.properties",
????????????????"bussiness.properties",
????????????????"blog.yml"
????????};
????????//循環(huán)添加
????????for?(String?profile?:?profiles)?{
????????????//從classpath路徑下面查找文件
????????????Resource?resource?=?new?ClassPathResource(profile);
????????????//加載成PropertySource對象,并添加到Environment環(huán)境中
????????????environment.getPropertySources().addLast(loadProfiles(resource));
????????}
????}
????//加載單個配置文件
????private?PropertySource<?>?loadProfiles(Resource?resource)?{
????????if?(!resource.exists())?{
????????????throw?new?IllegalArgumentException("資源"?+?resource?+?"不存在");
????????}
????????if(resource.getFilename().contains(".yml")){
????????????return?loadYaml(resource);
????????}?else?{
????????????return?loadProperty(resource);
????????}
????}
????/**
?????*?加載properties格式的配置文件
?????*?@param?resource
?????*?@return
?????*/
????private?PropertySource?loadProperty(Resource?resource){
????????try?{
????????????//從輸入流中加載一個Properties對象
????????????Properties?properties?=?new?Properties();
????????????properties.load(resource.getInputStream());
????????????return?new?PropertiesPropertySource(resource.getFilename(),?properties);
????????}catch?(Exception?ex)?{
????????????throw?new?IllegalStateException("加載配置文件失敗"?+?resource,?ex);
????????}
????}
????/**
?????*?加載yml格式的配置文件
?????*?@param?resource
?????*?@return
?????*/
????private?PropertySource?loadYaml(Resource?resource){
????????try?{
????????????YamlPropertiesFactoryBean?factory?=?new?YamlPropertiesFactoryBean();
????????????factory.setResources(resource);
????????????//從輸入流中加載一個Properties對象
????????????Properties?properties?=?factory.getObject();
????????????return?new?PropertiesPropertySource(resource.getFilename(),?properties);
????????}catch?(Exception?ex)?{
????????????throw?new?IllegalStateException("加載配置文件失敗"?+?resource,?ex);
????????}
????}
}
接著,在resources資源目錄下,我們還需要創(chuàng)建一個文件META-INF/spring.factories,通過spi方式,將自定義環(huán)境處理類加載到Spring處理器里面,當項目啟動時,會自動調(diào)用這個類!
#啟用我們的自定義環(huán)境處理類 org.springframework.boot.env.EnvironmentPostProcessor=com.example.property.env.MyEnvironmentPostProcessor

這種自定義環(huán)境處理類方式,相對會更佳靈活,首先編寫一個通用的配置文件解析類,支持properties和yml文件的讀取,然后將其注入到Spring容器里面,基本上可以做到一勞永逸!
2.5 最后,我們來介紹一下yml文件讀取
在上文中,我們大部分都是以properties為案例進行介紹,可能有的人已經(jīng)踩過坑了,在項目中使用@PropertySource注解來加載yml文件,結(jié)果啟動直接報錯,原因是@PropertySource不支持直接解析yml文件,只能解析properties文件。
那如果,我想單獨解析yml文件,也不想弄一個【自定義環(huán)境處理類】這種方式來讀取文件,應該如何處理呢?
操作方式也很簡單,以自定義的blog.yml文件為例!
blog.yml文件內(nèi)容:
pzblog: ??name:?helloWorld
然后,創(chuàng)建一個讀取yml文件的配置類
@Configuration
public?class?ConfigYaml?{
????/**
?????*?加載YML格式自定義配置文件
?????*?@return
?????*/
????@Bean
????public?static?PropertySourcesPlaceholderConfigurer?properties()?{
????????PropertySourcesPlaceholderConfigurer?configurer?=?new?PropertySourcesPlaceholderConfigurer();
????????YamlPropertiesFactoryBean?yaml?=?new?YamlPropertiesFactoryBean();
????????yaml.setResources(new?ClassPathResource("blog.yml"));
????????configurer.setProperties(yaml.getObject());
????????return?configurer;
????}
}
讀取數(shù)據(jù)的方式,與之類似!
@RestController
public?class?HelloController?{
????
????@Value("${pzblog.name}")
????private?String?pzblogName;
????@GetMapping("pzblogName")
????public?String?pzblogName(){
????????return?JSON.toJSONString(pzblogName);
????}
}以上就是SpringBoot注入自定義的配置文件的方法詳解的詳細內(nèi)容,更多關(guān)于SpringBoot注入配置文件的資料請關(guān)注腳本之家其它相關(guān)文章!
- 解決springboot配置文件組解決自動配置屬性無法注入問題
- 詳解SpringBoot依賴注入和使用配置文件
- Springboot根據(jù)配置文件動態(tài)注入接口實現(xiàn)類詳解
- springboot配置文件中使用${}注入值的兩種方式小結(jié)
- SpringBoot?配置文件給實體注入值方式
- springboot?yml配置文件值注入方式
- springboot注入yml配置文件 list報錯的解決方案
- 使用Springboot根據(jù)配置文件動態(tài)注入接口實現(xiàn)類
- SpringBoot注入配置文件的3種方法詳解
- SpringBoot配置文件注入值的簡單實現(xiàn)
相關(guān)文章
flowable動態(tài)創(chuàng)建多級流程模板實現(xiàn)demo
這篇文章主要為大家介紹了flowable動態(tài)創(chuàng)建多級流程模板實現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
Java關(guān)鍵字詳解之final static this super的用法
this用來調(diào)用目前類自身的成員變量,super多用來調(diào)用父類的成員,final多用來定義常量用的,static定義靜態(tài)變量方法用的,靜態(tài)變量方法只能被類本身調(diào)用,下文將詳細介紹,需要的朋友可以參考下2021-10-10
Spring Boot整合FTPClient線程池的實現(xiàn)示例
這篇文章主要介紹了Spring Boot整合FTPClient線程池的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12
Java并發(fā)編程中的volatile關(guān)鍵字詳解
這篇文章主要介紹了Java并發(fā)編程中的volatile關(guān)鍵字詳解,volatile?用于保證我們某個變量的可見性,使其一直存放在主存中,不被移動到某個線程的私有工作內(nèi)存中,需要的朋友可以參考下2023-08-08
關(guān)于接口ApplicationContext中的getBean()方法使用
這篇文章主要介紹了關(guān)于接口ApplicationContext中的getBean()方法使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
Java使用備忘錄模式實現(xiàn)過關(guān)類游戲功能詳解
這篇文章主要介紹了Java使用備忘錄模式實現(xiàn)過關(guān)類游戲功能,結(jié)合實例形式詳細分析了java備忘錄模式的概念、原理及其在過關(guān)類游戲中的具體應用方法,需要的朋友可以參考下2018-04-04

