Java Apollo是如何实现配置更新的

时间:2022-04-10 09:18:05

这篇文档主要关注下配置修改后对应的 Java 对象是如何更新,并不关注整体的配置改动流程

所有代码都来自 apollo-client 项目

更新流程

在 Apollo 控制台进行配置修改并发布后,对应的 client 端拉取到更新后,会调用到 com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener#onChange 方法

在调用 onChange 会收到对应的修改的配置信息 ConfigChangeEvent, 其中包含改动的 key 和 value, 则改动流程如下:

  1. 根据改动的配置的 key 从 springValueRegistry 找到对应的关联到这个 key 的 Spring Bean 信息,如果找不到则不处理
  2. 根据找到的 Spring Bean 信息,进行对应关联配置的更新

在第二步中会判断关联配置是用过属性关联还是方法进行关联的,代码如下

?
1
2
3
4
5
6
7
public void update(Object newVal) throws IllegalAccessException, InvocationTargetException {
  if (isField()) {
    injectField(newVal);
  } else {
    injectMethod(newVal);
  }
}

在上面的问题中,还有两个问题存疑

  1. 如何通过 key 找到对应的 Spring Bean 信息
  2. 如何将 Apollo 的配置值转换为 Spring 的识别的值
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
 private static final Logger logger = LoggerFactory.getLogger(AutoUpdateConfigChangeListener.class);
 
 private final boolean typeConverterHasConvertIfNecessaryWithFieldParameter;
 private final Environment environment;
 private final ConfigurableBeanFactory beanFactory;
 private final TypeConverter typeConverter;
 private final PlaceholderHelper placeholderHelper;
 private final SpringValueRegistry springValueRegistry;
 private final Gson gson;
 
 public AutoUpdateConfigChangeListener(Environment environment, ConfigurableListableBeanFactory beanFactory){
  this.typeConverterHasConvertIfNecessaryWithFieldParameter = testTypeConverterHasConvertIfNecessaryWithFieldParameter();
  this.beanFactory = beanFactory;
  this.typeConverter = this.beanFactory.getTypeConverter();
  this.environment = environment;
  this.placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
  this.springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
  this.gson = new Gson();
 }
 
 @Override
 public void onChange(ConfigChangeEvent changeEvent) {
  Set<String> keys = changeEvent.changedKeys();
  if (CollectionUtils.isEmpty(keys)) {
   return;
  }
  for (String key : keys) {
   // 1. check whether the changed key is relevant
   Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
   if (targetValues == null || targetValues.isEmpty()) {
    continue;
   }
 
   // 2. update the value
   for (SpringValue val : targetValues) {
    updateSpringValue(val);
   }
  }
 }
 
 private void updateSpringValue(SpringValue springValue) {
  try {
   Object value = resolvePropertyValue(springValue);
   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;
  }
 }
 
 private boolean testTypeConverterHasConvertIfNecessaryWithFieldParameter() {
  try {
   TypeConverter.class.getMethod("convertIfNecessary", Object.class, Class.class, Field.class);
  } catch (Throwable ex) {
   return false;
  }
  return true;
 }
}

如何将配置 key 和 Spring Bean 关联起来

在 Spring 常见配置包括 2 种

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ApiConfig {
 
    // 1. 直接在 Field 是进行注入
  @Value("${feifei.appId}")
  protected String appId;
 
  protected String predUrl;
 
    // 2. 在方法上进行注入
  @Value("${predUrl}")
  public void setPredUrl(String predUrl) {
    this.predUrl = predUrl;
  }
}

在 Apollo 代码中,通过实现 BeanPostProcessor 接口来检测所有的Spring Bean 的创建过程,在 Spring Bean 创建的过程中会调用对应的 org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization 方法。

Apollo 通过在 Bean 生成过程中,检测 Bean 类中属性和方法是否存在 @Value 注解,如果存在,提出其中的 key, 其处理方法在 processFieldprocessMethod 分别处理 Field 和 Method 中可能出现的 @Value 注解。如果存在注解则将对应的信息存到 SpringValue 对应 springValueRegistry 全局对象中,方便在其它地方可以直接获取。

在属性除了通过 @Value 注入,也可以用过 xml 进行配置,在这种情况通过 processBeanPropertyValues 方法来处理

通过两种处理方式就可以将 key 和对应的 Spring Bean 信息关联起来

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {
 
 private static final Logger logger = LoggerFactory.getLogger(SpringValueProcessor.class);
 
 private final ConfigUtil configUtil;
 private final PlaceholderHelper placeholderHelper;
 private final SpringValueRegistry springValueRegistry;
 
 private BeanFactory beanFactory;
 private Multimap<String, SpringValueDefinition> beanName2SpringValueDefinitions;
 
 public SpringValueProcessor() {
  configUtil = ApolloInjector.getInstance(ConfigUtil.class);
  placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
  springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
  beanName2SpringValueDefinitions = LinkedListMultimap.create();
 }
 
 @Override
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
   throws BeansException {
  if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() && beanFactory instanceof BeanDefinitionRegistry) {
   beanName2SpringValueDefinitions = SpringValueDefinitionProcessor
     .getBeanName2SpringValueDefinitions((BeanDefinitionRegistry) beanFactory);
  }
 }
 
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName)
   throws BeansException {
  if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
   super.postProcessBeforeInitialization(bean, beanName);
   processBeanPropertyValues(bean, beanName);
  }
  return bean;
 }
 
 
 @Override
 protected void processField(Object bean, String beanName, Field field) {
  // register @Value on field
  Value value = field.getAnnotation(Value.class);
  if (value == null) {
   return;
  }
  Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
 
  if (keys.isEmpty()) {
   return;
  }
 
  for (String key : keys) {
   SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
   springValueRegistry.register(beanFactory, key, springValue);
   logger.debug("Monitoring {}", springValue);
  }
 }
 
 @Override
 protected void processMethod(Object bean, String beanName, Method method) {
  //register @Value on method
  Value value = method.getAnnotation(Value.class);
  if (value == null) {
   return;
  }
  //skip Configuration bean methods
  if (method.getAnnotation(Bean.class) != null) {
   return;
  }
  if (method.getParameterTypes().length != 1) {
   logger.error("Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters",
     bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
   return;
  }
 
  Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());
 
  if (keys.isEmpty()) {
   return;
  }
 
  for (String key : keys) {
   SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, method, false);
   springValueRegistry.register(beanFactory, key, springValue);
   logger.info("Monitoring {}", springValue);
  }
 }
 
 
 private void processBeanPropertyValues(Object bean, String beanName) {
  Collection<SpringValueDefinition> propertySpringValues = beanName2SpringValueDefinitions
    .get(beanName);
  if (propertySpringValues == null || propertySpringValues.isEmpty()) {
   return;
  }
 
  for (SpringValueDefinition definition : propertySpringValues) {
   try {
    PropertyDescriptor pd = BeanUtils
      .getPropertyDescriptor(bean.getClass(), definition.getPropertyName());
    Method method = pd.getWriteMethod();
    if (method == null) {
     continue;
    }
    SpringValue springValue = new SpringValue(definition.getKey(), definition.getPlaceholder(),
      bean, beanName, method, false);
    springValueRegistry.register(beanFactory, definition.getKey(), springValue);
    logger.debug("Monitoring {}", springValue);
   } catch (Throwable ex) {
    logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(),
      definition.getPropertyName());
   }
  }
 
  // clear
  beanName2SpringValueDefinitions.removeAll(beanName);
 }
 
 @Override
 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  this.beanFactory = beanFactory;
 }
}

以上就是Java Apollo是如何实现配置更新的的详细内容,更多关于Java Apollo 配置更新的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/6934676052564246558