前面一篇博文介紹了一個@Value
的一些知識點降允,其中提了一個點,@Value
對應(yīng)的配置,除了是配置文件中之外递惋,可以從其他的數(shù)據(jù)源中獲取么搔弄,如從 redis竹宋,db救斑,http 中獲取配置泵琳?
了解過 SpringCloud Config 的可以給出確切的答案心褐,可以舔涎,而且用起來還老爽了,遠(yuǎn)程配置逗爹,支持配置動態(tài)刷新亡嫌,接下來我們來看一下,在 SpringBoot 中掘而,如何配置自定義的數(shù)據(jù)源
I. 項目環(huán)境
1. 項目依賴
本項目借助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
進行開發(fā)
開一個 web 服務(wù)用于測試
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
II. 自定義配置源
@Value
修飾的成員挟冠,綁定配置時,是從Envrionment
中讀取配置的袍睡,所以我們需要做的就是注冊一個自定義的配置源知染,借助MapPropertySource
可以來實現(xiàn)我們需求場景
1. 自定義數(shù)據(jù)源
演示一個最簡單自定義的配置數(shù)據(jù)源,重寫MapPropertySource
的getProperties
方法
實現(xiàn)如下
public class SimplePropertiesSource extends MapPropertySource {
public SimplePropertiesSource(String name, Map<String, Object> source) {
super(name, source);
}
public SimplePropertiesSource() {
this("filePropertiesSource", new HashMap<>());
}
/**
* 覆蓋這個方法女蜈,適用于實時獲取配置
*
* @param name
* @return
*/
@Override
public Object getProperty(String name) {
// 注意持舆,只針對自定義開頭的配置才執(zhí)行這個邏輯
if (name.startsWith("selfdefine.")) {
return name + "_" + UUID.randomUUID();
}
return super.getProperty(name);
}
}
2. 數(shù)據(jù)源注冊
上面只是聲明了配置源,接下來把它注冊到 Environment 中伪窖,這樣就可以供應(yīng)用使用了
@RestController
@SpringBootApplication
public class Application {
private Environment environment;
@Bean
public SimplePropertiesSource simplePropertiesSource(ConfigurableEnvironment environment) {
this.environment = environment;
SimplePropertiesSource ropertiesSource = new SimplePropertiesSource();
environment.getPropertySources().addLast(ropertiesSource);
return ropertiesSource;
}
// 獲取配置
@GetMapping(path = "get")
public String getProperty(String key) {
return environment.getProperty(key);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
從上面的輸出可以看出逸寓,自定義配置開頭的會獲取到隨機的配置值;非selfdefine
開頭的覆山,沒有相應(yīng)的配置竹伸,返回空
3. 基于文件的自定義配置源
上面這個可能有點過于兒戲了,接下來我們將配置源放在自定義的文件中簇宽,并支持文件配置修改
public class FilePropertiesSource extends MapPropertySource {
public FilePropertiesSource(String name, Map<String, Object> source) {
super(name, source);
}
public FilePropertiesSource() {
this("filePropertiesSource", new HashMap<>());
}
// 這種方式勋篓,適用于一次撈取所有的配置,然后從內(nèi)存中查詢對應(yīng)的配置魏割,提高服務(wù)性能
// 10s 更新一次
@PostConstruct
@Scheduled(fixedRate = 10_000)
public void refreshSource() throws IOException {
String ans =
FileCopyUtils.copyToString(new InputStreamReader(FilePropertiesSource.class.getClassLoader().getResourceAsStream("kv.properties")));
Map<String, Object> map = new HashMap<>();
for (String sub : ans.split("\n")) {
if (sub.isEmpty()) {
continue;
}
String[] kv = StringUtils.split(sub, "=");
if (kv.length != 2) {
continue;
}
map.put(kv[0].trim(), kv[1].trim());
}
source.clear();
source.putAll(map);
}
}
上面寫了一個定時器譬嚣,每 10s 刷新一下內(nèi)存中的配置信息,當(dāng)然這里也是可以配置一個文件變動監(jiān)聽器钞它,相關(guān)有興趣的話拜银,可以看下Java 實現(xiàn)文件變動的監(jiān)聽可以怎么玩
對應(yīng)的配置文件
user=xhh
name=一灰灰
age=18
注冊的姿勢與上面一致殊鞭,就不單獨說明了,接下來演示一下使用
從上可以看到文件中的配置修改之后尼桶,過一段時間會刷新
4. @Value
綁定自定義配置
接下來我們看一下操灿,將@Value
綁定自定義的配置,是否可以成功
調(diào)整一下上面的 Application, 添加一個成員屬性
@Value("${name}")
private String name;
@GetMapping(path = "get")
public String getProperty(String key) {
return name + "|" + environment.getProperty(key);
}
再次測試發(fā)現(xiàn)拋異常了泵督,說是這個配置不存在V貉巍!小腊!
(這就過分了啊救鲤,看了半天,結(jié)果告訴我不行溢豆,這還不得趕緊搞個差評么 ??????)
已經(jīng)寫到這里了蜒简,當(dāng)然我也得繼續(xù)嘗試挽救一下,為啥前面直接通過Environment
可以拿到配置漩仙,但是@Value
注解綁定就不行呢搓茬?
”罪魁禍?zhǔn)住熬驮谟诔跏蓟樞颍易远x的配置源队他,還沒有塞到Envrionment
卷仑,你就開會著手綁定了,就像準(zhǔn)備給”一灰灰 blog“一個差評麸折,結(jié)果發(fā)現(xiàn)還沒關(guān)注...(好吧锡凝,我承認(rèn)沒關(guān)注也可以評論 ??)
根據(jù)既往的知識點(至于是哪些知識點,那就長話短說不了了垢啼,看下面幾篇精選的博文吧)
- 【SpringBoot 基礎(chǔ)系列-實戰(zhàn)】如何指定 bean 最先加載(應(yīng)用篇)
- SpringBoot 系列教程之 Bean 之指定初始化順序的若干姿勢
- SpringBoot 系列教程之 Bean 加載順序之錯誤使用姿勢辟謠
要解決這個問題窜锯,一個最簡單的方式如下
創(chuàng)建一個獨立的配置類,實現(xiàn)自定義數(shù)據(jù)源的注冊
@Configuration
public class AutoConfig {
@Bean
public FilePropertiesSource filePropertiesSource(ConfigurableEnvironment environment) {
FilePropertiesSource filePropertiesSource = new FilePropertiesSource();
environment.getPropertySources().addLast(filePropertiesSource);
return filePropertiesSource;
}
}
測試類上指定 bean 依賴
@DependsOn("filePropertiesSource")
@EnableScheduling
@RestController
@SpringBootApplication
public class Application {
@Autowired
private Environment environment;
@Value("${name}")
private String name;
@GetMapping(path = "get")
public String getProperty(String key) {
return name + "|" + environment.getProperty(key);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
再次測試芭析,結(jié)果如下
從上面的演示動圖可以看到锚扎,綁定自定義的數(shù)據(jù)源配置,沒有問題馁启,但是驾孔,當(dāng)配置變更時,綁定的 name 字段惯疙,沒有隨之更新
簡單來講就是不支持動態(tài)刷新翠勉,這就難受了啊,我就想要動態(tài)刷新霉颠,那該怎么搞对碌?
- 不要急,新的博文已經(jīng)安排上了蒿偎,下篇奉上(怕迷路的小伙伴朽们,不妨關(guān)注一下”一灰灰 blog“??)
5. 小結(jié)
最后按照慣例小結(jié)一下克伊,本文篇幅雖長,但知識點比較集中华坦,總結(jié)下來,兩句話搞定
- 通過繼承
MapPropertySource
來實現(xiàn)自定義配置源不从,注冊到Envrionment
可供@Value
使用 - 使用
@Value
綁定自定義配置源時惜姐,注意注冊的順序要早于 bean 的初始化
好的,到這里正文結(jié)束椿息, 我是一灰灰歹袁,歡迎各位大佬來踩一踩長草的公眾號"一灰灰 blog"
III. 不能錯過的源碼和相關(guān)知識點
0. 項目
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 源碼: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-dynamic-envronment
配置系列博文
- 【SpringBoot 基礎(chǔ)系列】@Value 中哪些你不知道的知識點
- 【SpringBoot 基礎(chǔ)系列】ConfigurationProperties 配置綁定中那些你不知道的事情
- 【SpringBoot 基礎(chǔ)系列】PropertySource 加載 Yaml 配置文件實例演示
- 【SpringBoot 基礎(chǔ)系列】實現(xiàn)一個自定義配置加載器(應(yīng)用篇)
- SpringBoot基礎(chǔ)篇配置信息之配置刷新
- SpringBoot基礎(chǔ)篇配置信息之自定義配置指定與配置內(nèi)引用
- SpringBoot基礎(chǔ)篇配置信息之多環(huán)境配置信息
- SpringBoot基礎(chǔ)篇配置信息之如何讀取配置信息
1. 一灰灰 Blog
盡信書則不如,以上內(nèi)容寝优,純屬一家之言条舔,因個人能力有限,難免有疏漏和錯誤之處乏矾,如發(fā)現(xiàn) bug 或者有更好的建議孟抗,歡迎批評指正,不吝感激
下面一灰灰的個人博客钻心,記錄所有學(xué)習(xí)和工作中的博文凄硼,歡迎大家前去逛逛
- 一灰灰 Blog 個人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 專題博客 http://spring.hhui.top