Apollo源码解析——集成到springboot


前言

这里就不说SpringBoot自动装备的原理,ApolloSpringBoot的整合就是借助了SpringBoot的SPI机制,下面我们来看下处理流程


一、SpringBoot集成apollo源码分析

1. spring.factories

我们都知道spring.factoriesSpringBoot的扩展点,是实现自动装配的核心原理,Apollo客户端就是借助这个SPI机制与SpringBoot整合,我们看下apollojar包中的META-INFO/spring.factories文件
在这里插入图片描述

1.1 ApolloAutoConfiguration解析

这里看名字是实现apollo的自动配置,在配置类中会判断如果spring中没有PropertySourcesProcessor这个bean,那么他会new一个ConfigPropertySourcesProcessor到容器中,确保这个bean一定存在,这个bean的作用我们后续会解析

在这里插入图片描述

1.2ApolloApplicationContextInitializer解析

ApolloApplicationContextInitializer实现了两个接口ApplicationContextInitializerEnvironmentPostProcessorSpring Boot启动过程,会先调用EnvironmentPostProcessor.postProcessEnvironment方法,再调用ApplicationContextInitializer.initialize方法。也就是Spring Boot优先准备环境,再初始化容器。

1.2.1 ApolloApplicationContextInitializer#postProcessEnvironment

这里首先就是检查apollo.bootstrap.eagerLoad.enabled属性,判断是否允许在Spring启动初期加载apollo配置,如果允许则继续检查apollo.bootstrap.enabled属性判断是否允许加载apollo,如果都允许,则调用initialize方法进行初始化

  @Override
  public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {

    // should always initialize system properties like app.id in the first place
    initializeSystemProperty(configurableEnvironment);

    Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);

    //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
    if (!eagerLoadEnabled) {
      return;
    }

    Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);

    if (bootstrapEnabled) {
      DeferredLogger.enable();
      //初始化Apollo的配置
      initialize(configurableEnvironment);
    }

  }
1.2.2 ApolloApplicationContextInitializer#initialize
  1. 这里会发现他从apollo.bootstrap.namespaces属性配置中获取namespaces,然后遍历命名空间列表,调用ConfigService$getConfig拉取远程配置,然后将拉取到的配置包装成PropertySource,然后将包装好的PropertySource添加到CompositePropertySource内部,最后 CompositePropertySource 属性源包装类添加到 Spring 的 Environment 环境中,注意是插入在属性源列表的头部,因为取属性的时候其实是遍历这个属性源列表来查找,找到即返回,所以出现同名属性是以前面的为准
  protected void initialize(ConfigurableEnvironment environment) {

    if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
      //already initialized, replay the logs that were printed before the logging system was initialized
      DeferredLogger.replayTo();
      return;
    }

    String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
    logger.debug("Apollo bootstrap namespaces: {}", namespaces);
    List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

    CompositePropertySource composite;
    final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
    if (configUtil.isPropertyNamesCacheEnabled()) {
      composite = new CachedCompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
    } else {
      composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
    }
    for (String namespace : namespaceList) {
      //获取配置
      Config config = ConfigService.getConfig(namespace);

      composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
    }

    environment.getPropertySources().addFirst(composite);
  }
1.2.2.1 configPropertySourceFactory#getConfigPropertySource
  1. ApolloConfig封装成Spring ConfigConfigPropertySource对象
  2. 将新生成的ConfigPropertySource对象添加到内部列表,方便后续为每个配置实例添加配置变化监听器。
  public ConfigPropertySource getConfigPropertySource(String name, Config source) {
    //将Apollo的Config配置封装为继承自Spring内置的EnumerablePropertySource类的ConfigPropertySource对象
    ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);
    //将新生成的ConfigPropertySource对象缓存到内部列表,以备后续为每个配置实例添加配置变化监听器使用。
    configPropertySources.add(configPropertySource);

    return configPropertySource;
  }
1.2.2.2 ConfigService#getConfig
  1. 这里getManager去获取ConfigManager实例,采用了javaServiceLoader机制,是SPI机制
  2. 这里看ConfigManager的实现类其实只有一个,就是DefaultConfigManager
  public static Config getConfig(String namespace) {
    return s_instance.getManager().getConfig(namespace);
  }
  private ConfigManager getManager() {
    //双重检查锁防止重复创建
    if (m_configManager == null) {
      synchronized (this) {
        if (m_configManager == null) {
          //使用ApolloInjector获取ConfigManager实例,这里是采用了java的ServiceLoeader机制
          m_configManager = ApolloInjector.getInstance(ConfigManager.class);
        }
      }
    }

    return m_configManager;
  }
1.2.2.2 DefaultConfigManager#getConfig
  1. 这里首先从缓存中获取配置,缓存中没有则从远程拉取
  2. 这里使用SPI机制获取Factory,默认是DefaultConfigFactory,这里会调用DefaultConfigFactory#create从远程拉取配置
  public Config getConfig(String namespace) {
    Config config = m_configs.get(namespace);

    if (config == null) {
      synchronized (this) {
        config = m_configs.get(namespace);

        if (config == null) {
          //这里Factory创建也使用了ServiceLoader机制,默认是DefaultConfigFactory
          ConfigFactory factory = m_factoryManager.getFactory(namespace);

          config = factory.create(namespace);
          m_configs.put(namespace, config);
        }
      }
    }

    return config;
  }
1.2.2.3 DefaultConfigFactory#create

1.这里重点是首先调用createLocalConfigRepository(namespace)会创建LocalConfigRepository,在createLocalConfigRepository()方法中又会调用createRemoteConfigRepository(namespace)创建RemoteConfigRepository,最后又会调用createRepositoryConfig创建DefaultConfig对象,内部持有LocalConfigRepository
2. 对象创建时序:DefaultConfig -> LocalFileConfigRepository -> RemoteConfigRepository
3. 对象关系:DefaultConfig -> 持有 LocalFileConfigRepository, LocalFileConfigRepository -> 持有 RemoteConfigRepository
3. 配置变化传播时序:RemoteConfigRepository -> LocalFileConfigRepository -> DefaultConfig -> ConfigChangeListener

  public Config create(String namespace) {
    //确定本地配置缓存文件的格式
    ConfigFileFormat format = determineFileFormat(namespace);
    if (ConfigFileFormat.isPropertiesCompatible(format)) {
      return this.createRepositoryConfig(namespace, createPropertiesCompatibleFileConfigRepository(namespace, format));
    }
    //这里会创建DefaultConfig,内部会持有->LocalFileConfigRepository,LocalFileConfigRepository内部会有RemoteConfigRepository对象
    return this.createRepositoryConfig(namespace, createLocalConfigRepository(namespace));
  }
1.2.2.4 DefaultConfigFactory#createLocalConfigRepository

LocalFileConfigRepository内部持有RemoteConfigRepository,这里RemoteConfigRepository之前我们已经解析过,构造方法中包含定时拉取和长轮询逻辑,这里初始化RemoteConfigRepository会去远程将配置拉取到本地缓存起来

  LocalFileConfigRepository createLocalConfigRepository(String namespace) {
    if (m_configUtil.isInLocalMode()) {
      logger.warn(
          "==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====",
          namespace);
      return new LocalFileConfigRepository(namespace);
    }
    //这里内部持有RemoteConfigRepository对象
    return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));
  }
1.2.2.5 LocalFileConfigRepository构造方法
  1. 这里会查找当前apiiId本地配置缓存文件,没有则创建一个
  2. 将创建完毕的RemoteConfigRepository设置为上游配置仓库
  3. 调用trySync方法尝试同步
  public LocalFileConfigRepository(String namespace, ConfigRepository upstream) {
    m_namespace = namespace;
    m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
    //查找当前appid本地配置缓存文件,没有则创建一个,然后设置到属性中
    this.setLocalCacheDir(findLocalCacheDir(), false);
    //这里将创建完成的RemoteConfigRepository对象设置为上游配置仓库
    this.setUpstreamRepository(upstream);
    //尝试同步配置
    this.trySync();
  }
1.2.2.6 LocalFileConfigRepository#setUpstreamRepository
  1. 这里重点会将自己注册到upstreamConfigRepositorychangeListeners中,这样RemoteConfigRepository中配置发生改变,就会感知到。
  public void setUpstreamRepository(ConfigRepository upstreamConfigRepository) {
    if (upstreamConfigRepository == null) {
      return;
    }
    //clear previous listener
    if (m_upstream != null) {
      m_upstream.removeChangeListener(this);
    }
    m_upstream = upstreamConfigRepository;
    //这里将本地仓库作为监听者添加到上游仓库的监听者列表中如果上游仓库有变化,就会通知对应的监听者
    upstreamConfigRepository.addChangeListener(this);
  }
1.2.2.7 LocalFileConfigRepository#trySync

最终会调用到sync方法,这个方法就是使用上游仓库的配置更新本地文件,将配置缓存到磁盘上。

  protected void sync() {
    //sync with upstream immediately
    //使用上游仓库的配置更新本地文件
    boolean syncFromUpstreamResultSuccess = trySyncFromUpstream();

    if (syncFromUpstreamResultSuccess) {
      return;
    }

    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncLocalConfig");
    Throwable exception = null;
    try {
      transaction.addData("Basedir", m_baseDir.getAbsolutePath());
      //最终将配置持久化成文件
      m_fileProperties = this.loadFromLocalCacheFile(m_baseDir, m_namespace);
      m_sourceType = ConfigSourceType.LOCAL;
      transaction.setStatus(Transaction.SUCCESS);
    } catch (Throwable ex) {
      Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
      transaction.setStatus(ex);
      exception = ex;
      //ignore
    } finally {
      transaction.complete();
    }

    if (m_fileProperties == null) {
      m_sourceType = ConfigSourceType.NONE;
      throw new ApolloConfigException(
          "Load config from local config failed!", exception);
    }
  }

2. apollo配置属性注入

2.1 apollo组件注入@EnableApolloConfig

SpringBoot中使用Apollo客户端会在启动类上标注@EnableApolloConfig注解,这里可以看到使用Import导入了一个ApolloConfigRegistrar

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
  /**
   * Apollo namespaces to inject configuration into Spring Property Sources.
   */
  String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};

  /**
   * The order of the apollo config, default is {@link Ordered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.
   * If there are properties with the same name in different apollo configs, the apollo config with smaller order wins.
   * @return
   */
  int order() default Ordered.LOWEST_PRECEDENCE;
}
2.2.1 ApolloConfigRegistrar
  1. setEnvironment方法将Environment环境保存到helper中
  2. registerBeanDefinitions方法调用helper#registerBeanDefinitions
  3. help的实现是DefaultApolloConfigRegistrarHelper
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

  private final ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);

  /**
   * 回调方法,会在Spring解析完BeanDefinition将其加载进容器的准备阶段回调,这个方法也相当于一个入口,真正完成Apollo组件注册的逻辑在
   * DefaultApolloConfigRegistrarHelper#registerBeanDefinitions() 方法中
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    helper.registerBeanDefinitions(importingClassMetadata, registry);
  }

  @Override
  public void setEnvironment(Environment environment) {
    //将Environment环境保存到ApolloConfigRegistrarHelper中
    this.helper.setEnvironment(environment);
  }
2.2.2 DefaultApolloConfigRegistrarHelper#registerBeanDefinitions
  1. 首先解析@EnableApolloConfig注解,解析注解中的属性
  2. 调用工具类将Apollo相关组件类注册到spring容器中,关键组件有PropertySourcesProcessor(用于拉取@EnableApolloConfig配置的 namespace 的远程配置),ApolloAnnotationProcessor(用于处理Apollo的专用注解),SpringValueProcessor(用于处理@Value注解标注的类成员变量和方法),SpringValueDefinitionProcessor(处理XML文件中的占位符),PropertySourcesPlaceholderConfigurer(占位符处理,主要就是将@Vlaue(${ss.ss}))中解析出关键字,然后再使用keyPropertySourcesPropertyResolver PropertySource 中找到对应的属性值替换掉占位符
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //解析@EnableApolloConfig注解,将注解中配置的字符串数组处理为namespace数组,并调用PropertySourceProcessor.addNamespaces()方法将其缓存起来
    AnnotationAttributes attributes = AnnotationAttributes
            .fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
    final String[] namespaces = attributes.getStringArray("value");
    final int order = attributes.getNumber("order");
    final String[] resolvedNamespaces = this.resolveNamespaces(namespaces);
    PropertySourcesProcessor.addNamespaces(Lists.newArrayList(resolvedNamespaces), order);

    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
    propertySourcesPlaceholderPropertyValues.put("order", 0);

    //调用工具类尝试将Apollo组件类包装为BeanDefinition并注册到容器中,可以看到注册的Apollo关键组件有PropertySourcesProcessor(用于拉取
    // @EnableApolloConfig配置的 namespace 的远程配置),ApolloAnnotationProcessor(用于处理 Apollo的专用注解),SpringValueProcessor(用于
    // 处理@Value 注解标注的类成员变量和对象方法),SpringValueDefinitionProcessor(用于处理xml文件中的占位符),注意组件对象的实例化有优先级顺序,getOrder()返回
    //的值越小,优先级越高,对象创建的实际也就越靠前
    //PropertySourcesPlaceholderConfigurer是 SpringBoot 框架自身的占位符处理配置,占位符的处理主要是将 ${apollo.value} 这样的字符串解析出 关键字 apollo.value,
    // 再使用这个 key 通过 PropertySourcesPropertyResolver 从 PropertySource 中找到对应的属性值替换掉占位符
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
            PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
            PropertySourcesProcessor.class);
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
            ApolloAnnotationProcessor.class);
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(),
            SpringValueProcessor.class);
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(),
            SpringValueDefinitionProcessor.class);
  }
2.2.3 PropertySourcesProcessor#postProcessBeanFactory
  1. 拉取远程 namespace配置并初始化PropertySources
  2. 给所有缓存在本地的Config配置添加监听器
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        this.configUtil = ApolloInjector.getInstance(ConfigUtil.class);
        //拉取远程namespace的配置
        initializePropertySources();
        //给所有缓存在本地的Config配置添加监听器
        initializeAutoUpdatePropertiesFeature(beanFactory);
    }
2.2.3.1 PropertySourcesProcessor#initializePropertySources

这个上面已经分析过了,首先就是拉取配置,然后将拉取到的配置包装成PropertySource,然后添加到CompositePropertySource内部,然后将CompositePropertySource添加到springEnvvironment

    private void initializePropertySources() {
        if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) {
            //already initialized
            return;
        }
        CompositePropertySource composite;
        if (configUtil.isPropertyNamesCacheEnabled()) {
            composite = new CachedCompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
        } else {
            composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
        }

        //sort by order asc
        ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
        Iterator<Integer> iterator = orders.iterator();

        while (iterator.hasNext()) {
            int order = iterator.next();
            for (String namespace : NAMESPACE_NAMES.get(order)) {
                //依次拉取每个命名空间的远程配置
                Config config = ConfigService.getConfig(namespace);
                //调用 ConfigPropertySourceFactory#getConfigPropertySource() 缓存从远端拉取的配置,并将其包装为 PropertySource
                // ,最终将所有拉取到的远端配置聚合到一个以 ApolloPropertySources 为 key 的属性源包装类 CompositePropertySource 的内部
                composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
            }
        }

        // clean up
        NAMESPACE_NAMES.clear();

        // add after the bootstrap property source or to the first
        if (environment.getPropertySources()
                .contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {

            // ensure ApolloBootstrapPropertySources is still the first
            ensureBootstrapPropertyPrecedence(environment);

            environment.getPropertySources()
                    .addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite);
        } else {
            //将 CompositePropertySource 属性源包装类添加到 Spring 的 Environment 环境中,此处要保证 Bootstrap 的 namespace 配置如存在的话必须在属性源列表头部
            environment.getPropertySources().addFirst(composite);
        }
    }
2.2.3.2 PropertySourcesProcessor#initializeAutoUpdatePropertiesFeature

这里会给拉取到的所有配置添加自动更新的监听器

    private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
        if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() ||
                !AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
            return;
        }

        AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(
                environment, beanFactory);

        List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
        //给所有的apollo的配置对象添加自动更新的监听器
        for (ConfigPropertySource configPropertySource : configPropertySources) {
            configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
        }
    }
2.2.4 SpringValueProcessor#postProcessBeforeInitialization

这里就是将@Vlaue标注的bean字段缓存下来,然后通过反射使用配置替换原本的值,然后后续如果对应的Apollo配置有变化,也可以通过监听器感知到然后通过反射更新

  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
      super.postProcessBeforeInitialization(bean, beanName);
      processBeanPropertyValues(bean, beanName);
    }
    return bean;
  }
2.2.4.1 super#postProcessBeforeInitialization
  public Object postProcessBeforeInitialization(Object bean, String beanName)
          throws BeansException {
    //这个就是将被@Value标注的Bean字段缓存下来,如果后续这个字段对应的Apollo配置有更新,
    //可以通过反射将更新后的值更新为字段值
    Class clazz = bean.getClass();
    //遍历所有的属性
    for (Field field : findAllField(clazz)) {
      processField(bean, beanName, field);
    }
    //遍历所有的方法
    for (Method method : findAllMethod(clazz)) {
      processMethod(bean, beanName, method);
    }
    return bean;
  }
2.2.4.2 SpringValueProcessor#processField

这里处理属性和方法逻辑一致,就是判断有没有注解,存在就解析出来注解中的key,然后将关系封装成SpringValue并缓存起来

  protected void processField(Object bean, String beanName, Field field) {
    // register @Value on field
    //判断属性上面有没有@Value注解
    Value value = field.getAnnotation(Value.class);
    if (value == null) {
      return;
    }
    //如果注解存在,将注解占位符字符串中的属性Key解析出来,
    Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

    if (keys.isEmpty()) {
      return;
    }
    //对于每一个key,将其和Field字段等关键信息封装为SpringValue对象,并通过springValueRegistry#register缓存起来
    for (String key : keys) {
      SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
      springValueRegistry.register(beanFactory, key, springValue);
      logger.debug("Monitoring {}", springValue);
    }
  }
2.2.4.3 SpringValueProcessor#processBeanPropertyValues

这里和上面逻辑类似,就是检查有没有手动设置SpringValueDefinition,如果有,则将其转成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);
  }
2.2 apollo配置属性自动变更

在上面解析的时候说过对每一个配置都会设置一个自动配置更新的监听器AutoUpdateConfigChangeListener,这个监听器实际会设置到AbstractConfig中。
之前解析RemoteConfigRepository说过,当定时或者长轮询服务收到配置变化的时候,会调用fireRepositoryChange将触发监听器,将配置变化通知出去。

2.2.1 RemoteConfigRepository#fireRepositoryChange

这里触发监听器,对应的LocalFileConfigRepository之前已经注册了,所以会感知到。

    protected void fireRepositoryChange(String namespace, Properties newProperties) {
        for (RepositoryChangeListener listener : m_listeners) {
            try {
                // 触发监听器
                listener.onRepositoryChange(namespace, newProperties);
            } catch (Throwable ex) {
                Tracer.logError(ex);
                logger.error("Failed to invoke repository change listener {}", listener.getClass(), ex);
            }
        }
    }
2.2.2 LocalFileConfigRepository#onRepositoryChange

这里首先会根据配置的更新修改本地文件,然后再将事件传播出去,这里DefaultConfig会感知到

  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);
  }
2.2.3 DefaultConfig#onRepositoryChange

这里解析Properties,然后计算出实际变更的属性值,然后将变更后的值传播出去。

  public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
    if (newProperties.equals(m_configProperties.get())) {
      return;
    }

    ConfigSourceType sourceType = m_configRepository.getSourceType();
    Properties newConfigProperties = propertiesFactory.getPropertiesInstance();
    newConfigProperties.putAll(newProperties);

    //更新配置属性,并计算出实际变更的属性
    Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties,
        sourceType);

    //check double checked result
    if (actualChanges.isEmpty()) {
      return;
    }

    //将配置变化传播出去
    this.fireConfigChange(m_namespace, actualChanges);

    Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
  }
2.2.4 DefaultConfig#fireConfigChange

这里首先找到监听这个配置的监听器,然后异步通知对应的监听器,因为之前我们给配置注册了一个AutoUpdateConfigChangeListener,所以会调用到AutoUpdateConfigChangeListener#onChange方法

    protected void fireConfigChange(String namespace, Map<String, ConfigChange> changes) {
        final Set<String> changedKeys = changes.keySet();
        final List<ConfigChangeListener> listeners = this.findMatchedConfigChangeListeners(changedKeys);

        // notify those listeners
        for (ConfigChangeListener listener : listeners) {
            Set<String> interestedChangedKeys = resolveInterestedChangedKeys(listener, changedKeys);
            InterestedConfigChangeEvent interestedConfigChangeEvent = new InterestedConfigChangeEvent(
                    namespace, changes, interestedChangedKeys);
            this.notifyAsync(listener, interestedConfigChangeEvent);
        }
    }
2.2.5 AutoUpdateConfigChangeListener#onChange

从配置变化事件 ConfigChangeEvent取出发生了变化的key,再检查是否属于当前 BeanFactory,这里用到了之前的注册器springValueRegistry
检查通过则调用AutoUpdateConfigChangeListener#updateSpringValue()更新@Value标注的字段/方法值

  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);
      }
    }
  }
2.2.6 AutoUpdateConfigChangeListener#updateSpringValue

首先调用 AutoUpdateConfigChangeListener#resolvePropertyValue() 方法借助 SpringBoot 的组件将 @Value 中配置的占位符替换为 PropertySource 中的对应 key 的属性值。
调用 SpringValue#update()方法实际完成属性值的更新,这里就是直接通过反射更改bean属性。

  private void updateSpringValue(SpringValue springValue) {
    try {
      //借助springboot的组件将@value中配置的占位符替换为PropertySource中的对应key的属性值,
      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);
    }
  }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值