Apollo 2 如何支持 @Value 注解自動更新


前言

Apollo 在 v0.10.0 版本后,支持自動更新。v0.10.0之前的版本在配置變化后不會重新注入,需要重啟才會更新。

也就是說,如果一個屬性加入了 @Value 注解,並且這個配置在配置中心也存在,那么,配置中心修改屬性值后,就會自動更新這個值。同時,有個開關可以控制這個功能是否關閉(默認開啟)。
配置文件中寫入 apollo.autoUpdateInjectedSpringProperties = false 即可關閉該功能。

如何實現?

此次提交的 PR 詳情可見 https://codecov.io/gh/ctripcorp/apollo/pull/972/diff.

大致先說下實現思路:

Apollo 實現了 BeanPostProcessor 接口,這個接口的作用則是每個 bean 初始化成前后做操作

postProcessBeforeInitialization 方法中,會取出這個 Bean 的所有屬性和方法,並判斷他們是否含有 @Value 注解從而進行處理

具體處理邏輯,則是: 將符合條件的屬性封裝成一個 SpringValue 對象,放在一個 Map 中。當 clien 檢測到配置發生變化時,就會更新這個 Map 里面的值,從而達到自動更新的目的。

當然,這只是大概的思路,具體細節則要復雜一些。接下來我們就說說具體的實現細節。

實現細節

相關類

  1. SpringValue @Value 注解的詳細信息數據結構。
  2. SpringValueRegistry @Value 注冊中心,保存了他的 key/value 機構。
  3. SpringValueDefinitionProcessor 針對 Spring 3.x 版本做的特殊操作。
  4. SpringValueProcessor 處理 @Value 注解的類。
  5. ConfigPropertySourcesProcessor 注冊 SpringValueProcessor 到容器。
  6. PropertySourcesProcessor 將 Config 和自動更新監聽器綁定,同時注入 Spring 環境。

邏輯步驟

  1. 無論是 XML 方式,注解方式,SpringBoot 方式,都會觸發注冊機制,即自動注冊 SpringValueProcessor 處理器到 Spring 容器(他主要是個 BeanPostProcessor)。這是 apollo 自定義的 @Value 處理器。

  2. 不僅僅注冊 SpringValueProcessor ,還注冊 PropertySourcesProcessor,這是一個 BeanFactoryPostProcessor,即在 Spring bean 工廠初始化后,可以進行修改的一個類。這里的時機比 SpringValueProcessor 早。

  3. PropertySourcesProcessor 在具體方法中,會初始化所有的 Config(ConfigService.getConfig(namespace)),並設置到 Spring 的環境中(存儲所有的 Property,且同名狀態下優先級最高,目的是讓 Spring 自己注入到變量中)。同時,創建一個自動更新監聽器,監聽所有的 Config。

  4. SpringValueProcessor 在容器初始化 Bean 的時候,會處理所有帶有 @Value 注解的類,並放入到 SpringValueRegistry 的 Map 中。注意:SpringValueRegistry 是單例的。而 自動更新監聽器 也是包含一個 SpringValueRegistry 的。因此,每當一個 Config 變化的時候,都會觸發 change 事件,並調用監聽器的 onChange 方法,如果匹配,該方法則會更新 SpringValueRegistry 內部的值——完成自動更新。

  5. 這里有個問題:所有的配置都放在 SpringValueRegistry 中,一個 client 會持有多個 namespace,每個 namespace 可能會有重名的配置。那么會不會發生沖突呢?實際上,apollo 考慮到了這點,在設計 namespace 的時候,就有一個 order 屬性,用於處理這種情況,order 越小,優先級越高。當一個配置沒有顯時的設置 namespace 時,apollo 將其歸納為優先級最高的 namespace——即 order 最小的 namespace。而實現優先級的則是 Spring PropertySource 內部的排序機制,說白了就是一個 List,優先級最高的下標為0,當循環匹配的時候,優先匹配下標為0的配置。

  6. shouldTriggerAutoUpdate 方法里,有個判斷很繞:根據 key 獲取 Spring 環境中的配置值,判斷這個值和剛剛發生的變化值是否相等,如果相等,就更新 value,反之,跳過此次事件
    解釋一下:我們知道,@value 對應的是優先級最高的 namespaceenvironment 獲取的也是優先級最高的 namespace 的配置。
    如果一個配置更新了,但 environment 優先級最高的配置卻沒有更新,那么 @value 對應的更新事件就不應該觸發。
    如果一個配置更新了,environment 獲取到的優先級最高的配置也更新了,那么 @value 對應的更新時間就應該觸發。
    這里,其實就是 @value 是否更新的重要判斷。

總結

關於這個小功能,為什么要單獨拉出來說一說呢?實際上,該功能涉及到的東西還是很多的。例如:

  1. 一個普通的 Java 項目如何和 Spring 框架融合 —— 注冊,初始化,注入 Spring 環境等操作。
  2. 如何玩轉 Spring 的 PropertySource 的 order 機制。
  3. 每一個 namespace 都有一個長輪詢,發生更新后,apollo 觸發監聽器的更新事件,其中包括自動更新監聽器,但是,需要通過 shouldTriggerAutoUpdate 的判斷才能進行更新,因為 @value 可能會和多個 namespace 重名,需要通過優先級來過濾。即:如果 Spring 環境中優先級最高的 config 更新了,那么 @value 對應的 field 就需要更新,反之,不能更新(namespace 不匹配)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM