巧用扩展点:
SpringValueProcessor实现了BeanFactoryPostProcessor接口的postProcessBeanfactory方法。其作用就是创建存放着bean名称作为key,SpringValueDefinition作为value的Multimap。
SpringValueProcessor还实现了BeanPostProcessor接口的
postProcessBeforeInitialization方法,该方法主要是将所有带有@value注解的属性或者方法,放到配置项名称作为key,SpringValue作为Value的Multimap中。SpringValue存放了属性对应对应的Field,后面通过调用field.set(bean, newVal),进行值更新。
SpringValue类结构:
public class SpringValue {
private MethodParameter methodParameter;
//字段
private Field field;
//所属的bean对象,弱引用,可以在任何时刻被回收
private WeakReference<Object> beanRef;
private String beanName;
private String key;
private String placeholder;
private Class<?> targetType;
private Type genericType;
private boolean isJson;
}
更新处理:
在 Apollo 控制台进行配置修改并发布后,对应的 client 端拉取到更新后,会调用到
AutoUpdateConfigChangeListener类的onChange方法。
在调用onChange 收到对应的修改的配置信息 ConfigChangeEvent,其中包含改动的 key 和value, 则改动流程如下:
- 根据改动的配置的key从springValueRegistry找到对应的关联到这个key的Spring Bean 信息,如果找不到则不处理。
- 根据找到的 Spring Bean 信息,进行对应关联配置的更新。
代码如下:
@Override
public void onChange(ConfigChangeEvent changeEvent) {
//获取到值发生变更的配置项
Set<String> keys = changeEvent.changedKeys();
if (CollectionUtils.isEmpty(keys)) {
return;
}
for (String key : keys) {
// 1. 检查变更的配置项是否被注入到了Bean里面
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
if (targetValues == null || targetValues.isEmpty()) {
continue;
}
// 2. 一个配置项,可能会存在于多个Bean中,依次将所有的Bean都更新掉
for (SpringValue val : targetValues) {
updateSpringValue(val);
}
}
}
private void updateSpringValue(SpringValue springValue) {
try {
//获取最新的值
Object value = resolvePropertyValue(springValue);
//将最新的值更新到Bean中
springValue.update(value);
logger.info("Auto update apollo changed value successfully, new value: {}, {}", value,
springValue);
} catch (Throwable ex) {
logger.error("Auto update apollo changed value failed, {}", springValue.toString(), ex);
}
}
/**
* Logic transplanted from DefaultListableBeanFactory
* @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
*/
private Object resolvePropertyValue(SpringValue springValue) {
// value will never be null, as @Value and @ApolloJsonValue will not allow that
Object value = placeholderHelper
.resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder());
if (springValue.isJson()) {
value = parseJsonValue((String)value, springValue.getGenericType());
} else {
if (springValue.isField()) {
// org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+
if (typeConverterHasConvertIfNecessaryWithFieldParameter) {
value = this.typeConverter
.convertIfNecessary(value, springValue.getTargetType(), springValue.getField());
} else {
value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType());
}
} else {
value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(),
springValue.getMethodParameter());
}
}
return value;
}
private Object parseJsonValue(String json, Type targetType) {
try {
return gson.fromJson(json, targetType);
} catch (Throwable ex) {
logger.error("Parsing json '{}' to type {} failed!", json, targetType, ex);
throw ex;
}
}