Spring Cloud 配置變化監聽
背景
開發中遇到個需求,期望可以在配置變更的時候,監聽配置的變化,做一些邏輯處理,原生ApplicationEvent已經有發出對應的配置更新事件,但是包含的是所有的變更,開發人員一般只關心自己需要的配置變更
原生事件發出EnvironmentChangeEvent(如spring cloud config)或RefreshEvent(nacos 最終也會發EnvironmentChangeEvent事件)
對此我們可以基於EnvironmentChangeEvent,最一層包裝,封裝我們自己需要的事件
使用
先看下效果,直接基於EventListener捕獲ConfigRefreshEvent,condition使用el表達式配置需要監聽的key,針對集合或map 可以使用正則匹配
@EventListener(condition = "#event.key eq 'sys.loglevel.root'")
void handleConditionalListener(ConfigRefreshEvent event) {
// 業務邏輯 balabala
System.out.println("handleConditionalListener event key :" + event.getKey()
+ ", before :" + event.getBeforeRefresh()
+ ", after :" + event.getAfterRefresh());
}
/**
* LIST 或者 MAP 通過正則表達式匹配前綴
* LIST key爲 sys.loglevel.clazzLevel[0] sys.loglevel.clazzLevel[1] 類似格式
* MAP key 爲sys.loglevel.clazzLevel.key1 sys.loglevel.clazzLevel.key2 類似格式
* @param event
*/
@EventListener(condition = "{#event.key matches '^sys.loglevel.clazzLevel.*'}")
void clazzLogChangeListener(ConfigRefreshEvent event) {
log.info("receive log level change event, key :{}, value :{}", event.getKey(), event.getAfterRefresh());
}
需要的依賴
<dependency>
<groupId>com.github.yugj</groupId>
<artifactId>config-refresh-listener-core</artifactId>
<version>2.0.0</version>
</dependency>
參考代碼:https://github.com/yugj/spring-cloud-config-refresh-listener
實現原理
代碼很簡單直接一個監聽
/**
* 配置刷新監聽
* @author yugj
* @since 1.0-SNAPSHOT
*/
@Service
public class ConfigRefreshListener implements Ordered {
@Autowired
private Environment environment;
@Autowired
private ApplicationEventPublisher eventPublisher;
@EventListener(EnvironmentChangeEvent.class)
public void listener(EnvironmentChangeEvent event) {
for (String refreshKey : event.getKeys()) {
//查詢刷新配置的值,重新包裝爲自定義的ConfigRefreshEvent
Object afterRefreshed = environment.getProperty(refreshKey);
eventPublisher.publishEvent(new ConfigRefreshEvent(this,refreshKey, afterRefreshed, afterRefreshed));
}
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE - 1;
}
}
直接監聽最後的EnvironmentChangeEvent事件(spring cloud各自配置服務刷新最終都會轉換爲該事件),在該事件觸發的時候,包裝爲我們定義的ConfigRefreshEvent,然後依賴spring 事件監聽即可
項目中考慮到代碼簡單通用,沒有獲取變化前的數據
若需要獲取變化之前的數據可以參考org.springframework.cloud.context.refresh.ContextRefresher
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
//刷新之前的值,
Map<String, Object> before = extract(
this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
//變化的key值,在上面this.scope.refreshAll();之前可以通過變化的key去查詢before值獲取到變化之前的值
Set<String> keys = changes(before,
extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
return keys;
}
擴展使用
類似上面demo可以基於config server做日誌級別的調整及其他需要在進程內維護的數據監聽變更