一、RemoteConfigRepository同步配置流程回顾
@Override
protected synchronized void sync() {
ApolloConfig previous = m_configCache.get();
ApolloConfig current = loadApolloConfig();
if (previous != current) {
m_configCache.set(current);
this.fireRepositoryChange(m_namespace, this.getConfig());
}
}
二、LocalFileConfigRepository#onRepositoryChange更新本地配置
@Override
public void onRepositoryChange(String namespace, Properties newProperties) {
if (newProperties.equals(m_fileProperties)) {
return;
}
Properties newFileProperties = propertiesFactory.getPropertiesInstance();
newFileProperties.putAll(newProperties);
updateFileProperties(newFileProperties, m_upstream.getSourceType());
this.fireRepositoryChange(namespace, newProperties);
}
三、DefaultConfig#onRepositoryChange
@Override
public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
ConfigSourceType sourceType = m_configRepository.getSourceType();
Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
newConfigProperties.putAll(newProperties);
Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties, sourceType);
this.fireConfigChange(new ConfigChangeEvent(m_namespace, actualChanges));
}
四、ConfigChangeListener真正触发配置更新的地方
public interface ConfigChangeListener {
public void onChange(ConfigChangeEvent changeEvent);
}
4-1 AutoUpdateConfigChangeListener负责更新@Value和@ApolloJsonValue注解的方法和属性
- SpringValueRegistry中找到key对应的values
- 解析注解,反射更新
@Override
public void onChange(ConfigChangeEvent changeEvent) {
Set<String> keys = changeEvent.changedKeys();
for (String key : keys) {
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
for (SpringValue val : targetValues) {
updateSpringValue(val);
}
}
}
private void updateSpringValue(SpringValue springValue) {
Object value = resolvePropertyValue(springValue);
springValue.update(value);
}
4-2 @ApolloConfigChangeListener+RefreshScope更新@ConfigurationProperties修饰的配置类
- @ConfigurationProperties统一管理同一类配置,比如官方demo里
@ConfigurationProperties(prefix = "redis.cache")
@Component("sampleRedisConfig")
@RefreshScope
public class SampleRedisConfig {
private int expireSeconds;
private String clusterNodes;
private int commandTimeout;
}
- 而AutoUpdateConfigChangeListener不支持这样的配置类的更新,apollo是通过@ApolloConfigChangeListener+RefreshScope的方式来处理的,RefreshScope会根据beanName删除缓存中的bean实例,然后下次getBean时重新创建一个bean实例
@Component
public class SpringBootApolloRefreshConfig {
private final RefreshScope refreshScope;
public SpringBootApolloRefreshConfig(final RefreshScope refreshScope) {
this.refreshScope = refreshScope;
}
@ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION},
interestedKeyPrefixes = {"redis.cache."})
public void onChange(ConfigChangeEvent changeEvent) {
refreshScope.refresh("sampleRedisConfig");
}
}
- @ApolloConfigChangeListener收到通知其实也是通过ConfigChangeListener实现的,这里ConfigChangeListener是通过匿名内部类,在ApolloAnnotationProcessor的BeanPostProcessor阶段往Config对象注入的
- juejin.cn/post/686931… 的4-1中提到过
/**
* 1、从method上找到ApolloConfigChangeListener注解
* 2、创建ConfigChangeListener---目的是当配置发生变化时,反射调用ApolloConfigChangeListener注解的方法
* 3、注册ConfigChangeListener到每个Namespace的Config上
*/
@Override
protected void processMethod(final Object bean, String beanName, final Method method) {
ApolloConfigChangeListener annotation = AnnotationUtils
.findAnnotation(method, ApolloConfigChangeListener.class)
ReflectionUtils.makeAccessible(method)
String[] namespaces = annotation.value()
String[] annotatedInterestedKeys = annotation.interestedKeys()
String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes()
// 监听Apollo配置发生变化的Listener,当有配置发生变化会调用ConfigChangeListener的onChange方法
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ReflectionUtils.invokeMethod(method, bean, changeEvent)
}
}
Set<String> interestedKeys = annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null
Set<String> interestedKeyPrefixes = annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) : null
// 循环每个namespace,向他们的Config对象注册ConfigChangeListener
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace)
if (interestedKeys == null && interestedKeyPrefixes == null) {
config.addChangeListener(configChangeListener)
} else {
config.addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes)
}
}
}