Spring動態(tài)監(jiān)聽Nacos配置中心key值變更的實(shí)現(xiàn)方法
背景:
Nacos本身提供支持監(jiān)聽配置變更的操作,但在使用起來,個(gè)人感覺不是很友好,無法精確到某個(gè)key的變更監(jiān)聽
實(shí)現(xiàn)方法:
1. 自定義一個(gè)只能用在方法上的注解(當(dāng)然你也可以自定義不僅僅用在方法上)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConfigListener {
/**
* 待監(jiān)聽的key或key前綴
*/
String key();
}2. 添加具體實(shí)現(xiàn)
@Slf4j
@Configuration
public class RefreshEnvironmentConfig implements ApplicationListener<EnvironmentChangeEvent>, EnvironmentAware, BeanPostProcessor {
private Environment environment;
private final Map<String, Map<Method, Object>> listeners = new ConcurrentHashMap<>(64);
private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
Class<?> targetClass = AopUtils.getTargetClass(bean);
Map<Method, ConfigListener> annotatedMethods = MethodIntrospector.selectMethods(targetClass, this::findListenerAnnotations);
if (!annotatedMethods.isEmpty()) {
for (Map.Entry<Method, ConfigListener> entry : annotatedMethods.entrySet()) {
listeners.computeIfAbsent(entry.getValue().key(), k -> new HashMap<>()).put(entry.getKey(), bean);
log.info("Register @ConfigListener methods processed on bean {} listener key {}", beanName, entry.getValue().key());
}
}
this.nonAnnotatedClasses.add(bean.getClass());
}
return bean;
}
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
Set<String> keys = event.getKeys();
keys.forEach(key -> {
String value = environment.getProperty(key);
int index = 0;
do {
index = key.indexOf(".", index + 1);
String subKey = index > 0 ? key.substring(0, index) : key;
Map<Method, Object> methodMap = listeners.get(subKey);
if (methodMap != null) {
methodMap.forEach((method, bean) -> {
try {
method.invoke(bean, key, value);
} catch (IllegalAccessException | InvocationTargetException e) {
log.error("Refresh @ConfigListener {}, error {}", bean, e.getMessage(), e);
}
});
}
} while (index > 0);
});
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private ConfigListener findListenerAnnotations(Method method) {
return findListenerAnnotationsByAnnotatedElement(method);
}
private ConfigListener findListenerAnnotationsByAnnotatedElement(AnnotatedElement element) {
return AnnotationUtils.findAnnotation(element, ConfigListener.class);
}
}3. demo
@ConfigListener(key = "test.value")
public void listener1(String key, String newValue) {
log.info("listener1 listened {}-->{}", key, newValue);
}
@Async
@ConfigListener(key = "test")
public void listener2(String key, String newValue) {
log.info("listener2 listened {}-->{}", key, newValue);
}已上實(shí)現(xiàn),不僅僅適用于Nacos,理論上也適用于其他配置中心,(個(gè)人僅驗(yàn)證過Nacos)
拓展:spring監(jiān)聽nacos配置中心文件變化的兩種方式
1.前置條件
1.1依賴
<properties>
<spring-boot.version>2.4.2</spring-boot.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
spring-cloud-starter-bootstrap需要引入
1.2配置
1.2.1 nocos配置
我們要監(jiān)聽other.yaml文件的other.name|age屬性

1.2.2 bootstrap.yml文件內(nèi)容
server:
port: 8080
---
spring:
application:
#需要指定
name: gateway
profiles:
active: dev
---
spring:
cloud:
nacos:
discovery:
#需要配置
server-addr: 192.168.56.150:8848
group: DEFAULT_GROUP
config:
#需要配置
server-addr: 192.168.56.150:8848
group: DEFAULT_GROUP
file-extension: yaml
extension-configs:
- data-id: gateway-routes.yaml
group: DEFAULT_GROUP
refresh: true
- data-id: other.yaml
group: DEFAULT_GROUP
refresh: true
1.3屬性文件映射
@RefreshScope//Bean 在運(yùn)行時(shí)刷新
@ConfigurationProperties(prefix = "other")//要綁定外部屬性的前綴
@Configuration//設(shè)置為bean
public class Config {
//映射other.yaml ->oher.name
private String name;
//映射other.yaml ->oher.age
private String age;
//啟動后定時(shí)打印當(dāng)前bean中的值,不推薦這種方式,做測試方法
@PostConstruct
public void testValueIsChange() {
new Thread(() -> {
int i = 1;
do {
Config bean = SpringBeanUtil.getBean(Config.class);
System.out.println("--- >" + i + "-->當(dāng)前bean值: " + "name:" + bean.getName() + "|age:" + bean.getAge());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
i++;
} while (true);
}).start();
}
//....get/set方法
}
2監(jiān)聽
2.1第一種方式nacos的SDK
@Component
public class OtherListen {
@Resource//注入NacosConfigManager
public void nacosListen(NacosConfigManager nacosConfigManager) {
//獲取配置中心服務(wù)
ConfigService configService = nacosConfigManager.getConfigService();
try {
//對配置中心添加監(jiān)聽(配置文件的dataId,group)
configService.addListener("other.yaml", "DEFAULT_GROUP", new AbstractConfigChangeListener() {
//監(jiān)聽后的處理邏輯
@Override
public void receiveConfigChange(ConfigChangeEvent configChangeEvent) {
for (ConfigChangeItem changeItem : configChangeEvent.getChangeItems()) {
System.out.println("nacos方式監(jiān)聽" + changeItem.getKey() + "-----" + changeItem.getNewValue());
}
}
});
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
//注解監(jiān)聽spring.cloud環(huán)境下需要添加額外依賴,可以參考官方文檔,這里不做演示
@NacosConfigListener(dataId = "other.yaml")
public void onReceived(String value) {
System.out.println("onReceived : " + value);
}
}
2.2第二種方式監(jiān)聽spring的環(huán)境修改
//利用spring事件通知機(jī)制
@Component
public class OtherListen implements ApplicationListener<EnvironmentChangeEvent> {
@Resource
private ConfigurableEnvironment environment;
@Override
public void onApplicationEvent(EnvironmentChangeEvent event) {
for (String key : event.getKeys()) {
System.out.println("spring方式監(jiān)聽: key:" + key + "value:" + environment.getProperty(key));
}
}
}
3.測試
- 啟動后,正常讀取到nacos other.yaml數(shù)據(jù)

- 修改nacos other.yaml數(shù)據(jù)

- 查看監(jiān)聽是否正常
spring監(jiān)聽正常

nacosSDk監(jiān)聽正常

到此這篇關(guān)于Spring動態(tài)監(jiān)聽Nacos配置中心key值變更的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)Spring動態(tài)監(jiān)聽Nacos key值變更內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
聊聊ResourceBundle和properties讀取配置文件的區(qū)別
這篇文章主要介紹了ResourceBundle和properties讀取配置文件的區(qū)別,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式
這篇文章主要介紹了Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
JVM?jstack實(shí)戰(zhàn)之死鎖問題詳解
如果在生產(chǎn)環(huán)境發(fā)生了死鎖,我們將看到的是部署的程序沒有任何反應(yīng)了,這個(gè)時(shí)候我們可以借助?jstack進(jìn)行分析,下面我們實(shí)戰(zhàn)操作查找死鎖的原因2022-10-10
logback高效狀態(tài)管理器StatusManager源碼解析
這篇文章主要為大家介紹了logback高效狀態(tài)管理器StatusManager源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11

