配置動態重新整理@RefreshScope引起的取值為null
在Spring Cloud Config 動態重新整理demo編寫中,分為三個步驟:
1)git端配置更改,程式碼提交
2)手動重新整理配置: POST請求: http://localhost:3355/actuator/refresh
3)客戶端訪問配置:http://localhost:3355/testConfig
控制類程式碼如下:
1 @RestController
2 @RefreshScope
3 public class ConfigClientTestController {
4 @Value("${config.info}")
5 private String configInfo;
6
7 /**
8 * 讀取配置檔案內容
9 *
10 * @return
11 */
12 @GetMapping("testConfig")
13 private String configInfo() {
14 return configInfo;
15 }
16 }
這個時候發現問題,客戶端根本讀取不到配置。
分析:
1)@RefreshScope 註解是 Spring Cloud 中用於標記需要在配置發生變化時進行重新整理的 bean 的註解。當一個 bean 被 @RefreshScope 註解標記時,Spring 在建立這個 bean 的代理物件時會注入一些額外的邏輯,以便在配置變化時重新整理這個 bean 的屬性值。
2)代理物件是 Spring Framework 在執行時動態建立的物件,它包裝了真實的 bean 物件,並在需要時執行一些額外的操作,比如在配置變化時重新整理屬性值。在使用 @RefreshScope 註解時,Spring 建立了一個代理物件來管理被註解標記的 bean,並在需要時負責重新整理這個 bean 的屬性值。
3)當我們在呼叫方法時,實際上是呼叫了代理物件的方法,代理物件會負責處理實際的呼叫邏輯,包括在配置變化時重新整理屬性值。因此,透過方法呼叫可以獲取到最新的屬性值,但直接訪問 bean 的欄位可能會得到舊值或者 null,因為直接訪問的是代理物件的欄位,而不是真實的 bean 物件。
根據上面的分析,將程式碼再改動一下:
1 @RestController 2 @RefreshScope 3 public class ConfigClientTestController { 4 @Value("${config.info}") 5 private String configInfo; 6 7 public String getConfig() { 8 return configInfo; 9 } 10 11 /** 12 * 讀取配置檔案內容 13 * 14 * @return 15 */ 16 @GetMapping("testConfig") 17 private String configInfo() { 18 return getConfig(); 19 } 20 }
這樣就可以正常讀取值了。
@RefreshScope 會使注入的值放到代理類中,而當前bean的屬性欄位是沒有值的,直接讀取bean的field會為null,只有透過方法(不一定是get方法)才會觸發去代理類中取值
看到網上很多文章提到在@Controller中直接@Value獲取不到值,解決方法是定義另外一個配置類,再取值就可以了。
配置類:
1 @Component 2 @RefreshScope 3 public class ConfigData { 4 @Value("${config.info}") 5 private String configInfo; 6 7 public String getConfigInfo() { 8 return configInfo; 9 } 10 }
控制類:
1 @RestController 2 @RequiredArgsConstructor(onConstructor_ = {@Autowired}) 3 public class ConfigClientController { 4 5 private final ConfigData configData; 6 7 /** 8 * 讀取配置檔案內容 9 * 10 * @return 11 */ 12 @GetMapping("getConfigInfo") 13 private String configInfo() { 14 return configData.getConfigInfo(); 15 } 16 }
這樣也能正常讀取到手動重新整理後的配置值。不過,歸根結底,出現讀取不到最新值的原因其實跟取值方式有關, 都是代理惹的禍。
取值方式 | 無@RefreshScope | 有@RefreshScope |
方式1(field取值) | 有值 | null |
方式2(方法取值) | 有值 | 有值 |
參考連結: https://www.jianshu.com/p/a535f8250cb2