Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

# Apollo 的 value 注解是怎么自动更新的

发表于 2020-11-01 分类于 Java ， Apollo ， value 阅读次数： 阅读次数： Disqus：

在前司和目前公司，用的配置中心都是使用的 Apollo，经过了业界验证，比较强大的配置管理系统，特别是在0.10 后开始支持对使用 value 注解的配置值进行自动更新，今天刚好有个同学问到我，就顺便写篇文章记录下，其实也是借助于 spring 强大的 bean 生命周期管理，可以实现BeanPostProcessor接口，使用postProcessBeforeInitialization方法，来对bean 内部的属性和方法进行判断，是否有 value 注解，如果有就是将它注册到一个 map 中，可以看到这个方法`com.ctrip.framework.apollo.spring.annotation.SpringValueProcessor#processField`

    @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);
        }
      }

然后我们看下这个`springValueRegistry`是啥玩意

    public class SpringValueRegistry {
      private static final long CLEAN_INTERVAL_IN_SECONDS = 5;
      private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap();
      private final AtomicBoolean initialized = new AtomicBoolean(false);
      private final Object LOCK = new Object();

      public void register(BeanFactory beanFactory, String key, SpringValue springValue) {
        if (!registry.containsKey(beanFactory)) {
          synchronized (LOCK) {
            if (!registry.containsKey(beanFactory)) {
              registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create());
            }
          }
        }

        registry.get(beanFactory).put(key, springValue);

        // lazy initialize
        if (initialized.compareAndSet(false, true)) {
          initialize();
        }
      }

这类其实就是个 map 来存放 springvalue，然后有`com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener`来监听更新操作，当有变更时

    @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. check whether the value is really changed or not (since spring property sources have hierarchies)
         // 这里其实有一点比较绕，是因为 Apollo 里的 namespace 划分，会出现 key 相同，但是 namespace 不同的情况，所以会有个优先级存在，所以需要去校验 environment 里面的是否已经更新，如果未更新则表示不需要更新
         if (!shouldTriggerAutoUpdate(changeEvent, key)) {
           continue;
         }

         // 3. update the value
         for (SpringValue val : targetValues) {
           updateSpringValue(val);
         }
       }
     }

其实原理很简单，就是得了解知道下