前面一篇博文介紹了一個@Value
的一些知識點,其中提了一個點,@Value
對應的配置,除了是配置檔案中之外,可以從其他的資料來源中獲取麼,如從 redis,db,http 中獲取配置?
瞭解過 SpringCloud Config 的可以給出確切的答案,可以,而且用起來還老爽了,遠端配置,支援配置動態重新整理,接下來我們來看一下,在 SpringBoot 中,如何配置自定義的資料來源
I. 專案環境
1. 專案依賴
本專案藉助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
進行開發
開一個 web 服務用於測試
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
II. 自定義配置源
@Value
修飾的成員,繫結配置時,是從Envrionment
中讀取配置的,所以我們需要做的就是註冊一個自定義的配置源,藉助MapPropertySource
可以來實現我們需求場景
1. 自定義資料來源
演示一個最簡單自定義的配置資料來源,重寫MapPropertySource
的getProperties
方法
實現如下
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) {
// 注意,只針對自定義開頭的配置才執行這個邏輯
if (name.startsWith("selfdefine.")) {
return name + "_" + UUID.randomUUID();
}
return super.getProperty(name);
}
}
2. 資料來源註冊
上面只是宣告瞭配置源,接下來把它註冊到 Environment 中,這樣就可以供應用使用了
@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
開頭的,沒有相應的配置,返回空
3. 基於檔案的自定義配置源
上面這個可能有點過於兒戲了,接下來我們將配置源放在自定義的檔案中,並支援檔案配置修改
public class FilePropertiesSource extends MapPropertySource {
public FilePropertiesSource(String name, Map<String, Object> source) {
super(name, source);
}
public FilePropertiesSource() {
this("filePropertiesSource", new HashMap<>());
}
// 這種方式,適用於一次撈取所有的配置,然後從記憶體中查詢對應的配置,提高服務效能
// 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 重新整理一下記憶體中的配置資訊,當然這裡也是可以配置一個檔案變動監聽器,相關有興趣的話,可以看下Java 實現檔案變動的監聽可以怎麼玩
對應的配置檔案
user=xhh
name=一灰灰
age=18
註冊的姿勢與上面一致,就不單獨說明了,接下來演示一下使用
從上可以看到檔案中的配置修改之後,過一段時間會重新整理
4. @Value
繫結自定義配置
接下來我們看一下,將@Value
繫結自定義的配置,是否可以成功
調整一下上面的 Application, 新增一個成員屬性
@Value("${name}")
private String name;
@GetMapping(path = "get")
public String getProperty(String key) {
return name + "|" + environment.getProperty(key);
}
再次測試發現拋異常了,說是這個配置不存在!!!
(這就過分了啊,看了半天,結果告訴我不行,這還不得趕緊搞個差評麼 ???)
已經寫到這裡了,當然我也得繼續嘗試挽救一下,為啥前面直接通過Environment
可以拿到配置,但是@Value
註解繫結就不行呢?
”罪魁禍首“就在於初始化順序,我自定義的配置源,還沒有塞到Envrionment
,你就開會著手繫結了,就像準備給”一灰灰 blog“一個差評,結果發現還沒關注...(好吧,我承認沒關注也可以評論 ?)
根據既往的知識點(至於是哪些知識點,那就長話短說不了了,看下面幾篇精選的博文吧)
- 【SpringBoot 基礎系列-實戰】如何指定 bean 最先載入(應用篇)
- SpringBoot 系列教程之 Bean 之指定初始化順序的若干姿勢
- SpringBoot 系列教程之 Bean 載入順序之錯誤使用姿勢闢謠
要解決這個問題,一個最簡單的方式如下
建立一個獨立的配置類,實現自定義資料來源的註冊
@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);
}
}
再次測試,結果如下
從上面的演示動圖可以看到,繫結自定義的資料來源配置,沒有問題,但是,當配置變更時,繫結的 name 欄位,沒有隨之更新
簡單來講就是不支援動態重新整理,這就難受了啊,我就想要動態重新整理,那該怎麼搞?
- 不要急,新的博文已經安排上了,下篇奉上(怕迷路的小夥伴,不妨關注一下”一灰灰 blog“?)
5. 小結
最後按照慣例小結一下,本文篇幅雖長,但知識點比較集中,總結下來,兩句話搞定
- 通過繼承
MapPropertySource
來實現自定義配置源,註冊到Envrionment
可供@Value
使用 - 使用
@Value
繫結自定義配置源時,注意註冊的順序要早於 bean 的初始化
好的,到這裡正文結束, 我是一灰灰,歡迎各位大佬來踩一踩長草的公眾號"一灰灰 blog"
III. 不能錯過的原始碼和相關知識點
0. 專案
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 原始碼: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-dynamic-envronment
配置系列博文
- 【SpringBoot 基礎系列】@Value 中哪些你不知道的知識點
- 【SpringBoot 基礎系列】ConfigurationProperties 配置繫結中那些你不知道的事情
- 【SpringBoot 基礎系列】PropertySource 載入 Yaml 配置檔案例項演示
- 【SpringBoot 基礎系列】實現一個自定義配置載入器(應用篇)
- SpringBoot基礎篇配置資訊之配置重新整理
- SpringBoot基礎篇配置資訊之自定義配置指定與配置內引用
- SpringBoot基礎篇配置資訊之多環境配置資訊
- SpringBoot基礎篇配置資訊之如何讀取配置資訊
1. 一灰灰 Blog
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現 bug 或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛
- 一灰灰 Blog 個人部落格 https://blog.hhui.top
- 一灰灰 Blog-Spring 專題部落格 http://spring.hhui.top