遇到一个nacos配置动静更新map的问题,对于map的key能够新增,不能删除的问题排查,比方我有个
map:
a:a
b:b
这里我能够批改配置进行追加c:c,变成
map:
a:a
b:b
c:c
这样是能够的,然而不能删除,比方删除掉a:a,后果还是
map:
a:a
b:b
c:c
源码剖析一波,进行排查
首先要介绍一下spring的各生命周期周期,这里动静更新就用到了RefreshEvent事件,在NacosContextRefresher#registerNacosListener()里,nacos实现了一个批改配置的回调监听,并且会播送一个RefreshEvent事件
而后RefreshEventListener会收到事件,并调用ContextRefresher#refresh()->refreshEnvironment(),执行updateEnvironment(),从新加载Context的Environment,之后持续播送一个EnvironmentChangeEvent事件,ConfigurationPropertiesRebinder接管到该事件后,会调用rebind(),这里就是更改具体@ConfigurationProperties的配置类了,具体做法就是在该办法里调用了
this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean, name);
先destroy原来的bean,而后在从新初始化bean。而spring初始化bean的过程中就蕴含了一个BeanPostProcessor回调解决,其中实现类ConfigurationPropertiesBindingPostProcessor,就会对@ConfigurationProperties的配置类进行解决,把配置文件内容(比方application.yml,systemProperties等,有优先级程序,如果是同一个属性还会笼罩)和配置类字段进行bind,bind()->bindObject(),这里会依据类型判断执行哪种策略,首先是判断复合类型的
AggregateBinder有三种实现:MapBinder、CollectionBind、ArrayBinder
再而后是bindProperty(),应用的是spring的Converter,
最初是bindDataObject()
DataObjectBinder有两种实现:JavaBeanBinder、ValueObjectBinder
我的配置类的属性就是一个map类型,所以进入到MapBinder里,这里在调用AggregateBinder#bind()会执行子类MapBinder的merge()
问题就出在这个办法里,
@Override
protected Map<Object, Object> merge(Supplier<Map<Object, Object>> existing, Map<Object, Object> additional) {
Map<Object, Object> existingMap = getExistingIfPossible(existing);
if (existingMap == null) {
return additional;
}
try {
existingMap.putAll(additional);
return copyIfPossible(existingMap);
}
catch (UnsupportedOperationException ex) {
Map<Object, Object> result = createNewMap(additional.getClass(), existingMap);
result.putAll(additional);
return result;
}
}
能够看到这里existingMap.putAll(additional);是把旧的map.putAll新的map,对于删除操作,旧map的数据更全,新map的数据少一下
这时候进行put,所以数据和旧map一样,没有删除成果
那有没有方法实现删除呢,也是有的,就是在后面的从新初始化bean前回调的destroy,只有咱们在销毁bean时把map==null
则实现了全量删除再新增的成果,以此实现删除成果
@PreDestroy
public void destroy() {
map = null;
}
题外:能够看到咱们加载configuration配置时应用的是ConfigurationPropertySource类,然而咱们应用nacos的配置也好NacosPropertySource,还是其余配置也好,父类都是PropertySource,这里spring会把PropertySource包装成SpringConfigurationPropertySource(ConfigurationPropertySource的子类),具体可见SpringConfigurationPropertySource.from(source);
发表回复