# Apollo自动更新实现原理

版本:apollo-client 1.4.0

# 监听

监听

# RemoteConfigRepository&LocalFileConfigRepository

这里只对监听器RemoteConfigRepository进行详解,LocalFileConfigRepository比较简单,可自行查看

# 初始化

一个命名空间对应了一个Config类,在初始化命名空间的Config类的同时,就会初始化对应的监听器RemoteConfigRepository

  • DefaultConfigManager.getConfig
    • ApolloInjector.getInstance(ConfigFactoryManager.class).getFactory(namespace).create(namespace)
      • DefaultConfigFactory.create(String namespace)
        • DefaultConfigFactory.createLocalConfigRepository(namespace)

          • DefaultConfigFactory.createRemoteConfigRepository(namespace)

            // 重点1: 一个命名空间被初始化的时候,同时远程监听器就被创建出来了,对应的config为DefaultConfig

            • 调用RemoteConfigRepository构造方法

            // 重点2: 一个命名空间被初始化的时候, 本地文件的监听器也被创建出来,用于更新本地缓存

            • return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace))
        • new DefaultConfig(String namespace, ConfigRepository configRepository)

          // 加载配置文件

          • DefaultConfig.loadFromResource(m_namespace);

            • 从配置文件META-INF/config/{namespace}.properties加载对应命名空间的配置
          • DefaultConfig.initialize();

            // 将读取到的配置存储到PropertySource中

            • DefaultConfig.updateConfig(m_configRepository.getConfig(), m_configRepository.getSourceType());

            // 注册监听器

            • configRepository.addChangeListener(this);

# RemoteConfigRepository做了哪些事

  • 初始化配置
  • 初始化读取远程配置
  • 开启定时任务,定期读取远程配置
  • 长轮询,判定远程配置是否有更新
  • 刷新文件缓存
  • 刷新内存中的缓存

# 代码分析

省略了部分代码,只保留关键部分

  • 初始化
public RemoteConfigRepository(String namespace) {
    // 初始化一系列配置开始
    m_namespace = namespace;
    m_configCache = new AtomicReference<>();
    m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
    m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
    m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
    remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
    m_longPollServiceDto = new AtomicReference<>();
    m_remoteMessages = new AtomicReference<>();
    m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
    m_configNeedForceRefresh = new AtomicBoolean(true);
    m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
        m_configUtil.getOnErrorRetryInterval() * 8);
    gson = new Gson();
    // 初始化一系列配置结束
    
    // 程序启动时,首次读取apollo远程配置
    this.trySync();
    
    // 开始定时任务,定期获取远程的配置,判定是否有更新
    this.schedulePeriodicRefresh();
    
    // 长轮询,监听远程配置是否有更新
    this.scheduleLongPollingRefresh();
}
  • 定时任务

每5分钟获取一次远程配置,刷新缓存

private void schedulePeriodicRefresh() {
    logger.debug("Schedule periodic refresh with interval: {} {}",
        m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
    m_executorService.scheduleAtFixedRate(
        new Runnable() {
          @Override
          public void run() {
            Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
            logger.debug("refresh config for namespace: {}", m_namespace);
            trySync();
            Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
          }
        },5, 5, TimeUnit.MINUTES);
}
  • 长轮询
  1. 提交长轮询
  private void scheduleLongPollingRefresh() {
    remoteConfigLongPollService.submit(m_namespace, this);
  }

  public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) {
    m_longPollServiceDto.set(longPollNotifiedServiceDto);
    m_remoteMessages.set(remoteMessages);
    m_executorService.submit(new Runnable() {
      @Override
      public void run() {
        m_configNeedForceRefresh.set(true);
        trySync();
      }
    });
  }
  1. 开始长轮询
  private void startLongPolling() {
    if (!m_longPollStarted.compareAndSet(false, true)) {
      //already started
      return;
    }
    try {
        // ...
      m_longPollingService.submit(new Runnable() {
        @Override
        public void run() {
          if (longPollingInitialDelayInMills > 0) {
            try {
              logger.debug("Long polling will start in {} ms.", longPollingInitialDelayInMills);
              // 等待2s
              TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
              //ignore
            }
          }
          doLongPollingRefresh(appId, cluster, dataCenter);
        }
      });
    } catch (Throwable ex) {
   // ...
    }
  }
  1. 执行长轮询,读取远程的数据,读取成功之后更新本地的notification和remoteNotification
  private void doLongPollingRefresh(String appId, String cluster, String dataCenter) {
    final Random random = new Random();
    ServiceDTO lastServiceDto = null;
    while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) {
        //     由限流器判断判断调用次数,每秒调用2次
        //     m_longPollRateLimiter = RateLimiter.create(2);
      if (!m_longPollRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) {
        //wait at most 5 seconds
       // ....
      }
     // ...
      try {
        // ....

        // 读取apollo配置
        final HttpResponse<List<ApolloConfigNotification>> response =
            m_httpUtil.doGet(request, m_responseType);

        logger.debug("Long polling response: {}, url: {}", response.getStatusCode(), url);
        if (response.getStatusCode() == 200 && response.getBody() != null) {
            // 接口响应成功,通知更新
          updateNotifications(response.getBody());
          updateRemoteNotifications(response.getBody());
          transaction.addData("Result", response.getBody().toString());
          notify(lastServiceDto, response.getBody());
        }

        //try to load balance
        // .. 事务提交
      } catch (Throwable ex) {
        /// ....
      } finally {
       /// ....
      }
    }
  }
  1. 更新本地的notification,记录到ConcurrentMap中
  private final ConcurrentMap<String, Long> m_notifications = Maps.newConcurrentMap();

  private void updateNotifications(List<ApolloConfigNotification> deltaNotifications) {
    for (ApolloConfigNotification notification : deltaNotifications) {
      if (Strings.isNullOrEmpty(notification.getNamespaceName())) {
        continue;
      }
      String namespaceName = notification.getNamespaceName();
      if (m_notifications.containsKey(namespaceName)) {
        m_notifications.put(namespaceName, notification.getNotificationId());
      }
      //since .properties are filtered out by default, so we need to check if there is notification with .properties suffix
      String namespaceNameWithPropertiesSuffix =
          String.format("%s.%s", namespaceName, ConfigFileFormat.Properties.getValue());
      if (m_notifications.containsKey(namespaceNameWithPropertiesSuffix)) {
        m_notifications.put(namespaceNameWithPropertiesSuffix, notification.getNotificationId());
      }
    }
  }
  1. 更新远程的notification, 将结果记录到ConcurrentMap中
  private final Map<String, ApolloNotificationMessages> m_remoteNotificationMessages = Maps.newConcurrentMap();

  private void updateRemoteNotifications(List<ApolloConfigNotification> deltaNotifications) {
    for (ApolloConfigNotification notification : deltaNotifications) {
      if (Strings.isNullOrEmpty(notification.getNamespaceName())) {
        continue;
      }

      if (notification.getMessages() == null || notification.getMessages().isEmpty()) {
        continue;
      }

      ApolloNotificationMessages localRemoteMessages =
          m_remoteNotificationMessages.get(notification.getNamespaceName());
      if (localRemoteMessages == null) {
        localRemoteMessages = new ApolloNotificationMessages();
        m_remoteNotificationMessages.put(notification.getNamespaceName(), localRemoteMessages);
      }

      localRemoteMessages.mergeFrom(notification.getMessages());
    }
  }
  1. 通知更新
  private void notify(ServiceDTO lastServiceDto, List<ApolloConfigNotification> notifications) {
    if (notifications == null || notifications.isEmpty()) {
      return;
    }
    for (ApolloConfigNotification notification : notifications) {
      String namespaceName = notification.getNamespaceName();
      //create a new list to avoid ConcurrentModificationException
      List<RemoteConfigRepository> toBeNotified =
          Lists.newArrayList(m_longPollNamespaces.get(namespaceName));
      ApolloNotificationMessages originalMessages = m_remoteNotificationMessages.get(namespaceName);
      ApolloNotificationMessages remoteMessages = originalMessages == null ? null : originalMessages.clone();
      //since .properties are filtered out by default, so we need to check if there is any listener for it
      toBeNotified.addAll(m_longPollNamespaces
          .get(String.format("%s.%s", namespaceName, ConfigFileFormat.Properties.getValue())));
      // 遍历需要通知的配置
      for (RemoteConfigRepository remoteConfigRepository : toBeNotified) {
        try {
          remoteConfigRepository.onLongPollNotified(lastServiceDto, remoteMessages);
        } catch (Throwable ex) {
          Tracer.logError(ex);
        }
      }
    }
  }
  1. 执行通知

到这里我们就可以看到,这里和定时任务走到相同的逻辑了,trySync,下面我们看看trySync的逻辑

 public void onLongPollNotified(ServiceDTO longPollNotifiedServiceDto, ApolloNotificationMessages remoteMessages) {
    m_longPollServiceDto.set(longPollNotifiedServiceDto);
    m_remoteMessages.set(remoteMessages);
    m_executorService.submit(new Runnable() {
      @Override
      public void run() {
        m_configNeedForceRefresh.set(true);
        trySync();
      }
    });
  }
  • trySync【AbstractConfigRepository】
public abstract class AbstractConfigRepository implements ConfigRepository {
  private static final Logger logger = LoggerFactory.getLogger(AbstractConfigRepository.class);
  private List<RepositoryChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();

  protected boolean trySync() {
    try {
      sync();
      return true;
    } catch (Throwable ex) {
      Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
      logger
          .warn("Sync config failed, will retry. Repository {}, reason: {}", this.getClass(), ExceptionUtil
              .getDetailMessage(ex));
    }
    return false;
  }

  protected abstract void sync();

  @Override
  public void addChangeListener(RepositoryChangeListener listener) {
    if (!m_listeners.contains(listener)) {
      m_listeners.add(listener);
    }
  }

  @Override
  public void removeChangeListener(RepositoryChangeListener listener) {
    m_listeners.remove(listener);
  }

  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);
      }
    }
  }
}
  1. RemoteConfigRepository.sync
 @Override
  protected synchronized void sync() {
    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

    try {
        // 从缓存中获取配置
      ApolloConfig previous = m_configCache.get();
      
      // 调用一次接口,获取当前命名空间下,apollo最新的配置
      ApolloConfig current = loadApolloConfig();

      //reference equals means HTTP 304
        // 如果配置有更新,调用fireRepositoryChange进行更新
      if (previous != current) {
        logger.debug("Remote Config refreshed!");
        m_configCache.set(current);
        this.fireRepositoryChange(m_namespace, this.getConfig());
      }

      if (current != null) {
        Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
            current.getReleaseKey());
      }

      transaction.setStatus(Transaction.SUCCESS);
    } catch (Throwable ex) {
      transaction.setStatus(ex);
      throw ex;
    } finally {
      transaction.complete();
    }
  }
  1. DefaultConfig.onRepositoryChange更新本地缓存,同时调用changeListener的onChange方法进行bean字段更新
@Override
  public synchronized void onRepositoryChange(String namespace, Properties newProperties) {
    if (newProperties.equals(m_configProperties.get())) {
      return;
    }

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

    // 将命名空间中每个修改,新增,删除的配置一对一的封装成ConfigChange对象,并构造map,map的key为propertyName
    Map<String, ConfigChange> actualChanges = updateAndCalcConfigChanges(newConfigProperties, sourceType);

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

    this.fireConfigChange(new ConfigChangeEvent(m_namespace, actualChanges));

    Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
  }
  1. DefaultConfig.updateAndCalcConfigChanges
  private Map<String, ConfigChange> updateAndCalcConfigChanges(Properties newConfigProperties,
      ConfigSourceType sourceType) {
    // 将配置项转换成ConfigList,并设置变更类型,新增or删除or更新
    List<ConfigChange> configChanges =
        calcPropertyChanges(m_namespace, m_configProperties.get(), newConfigProperties);

    ImmutableMap.Builder<String, ConfigChange> actualChanges =
        new ImmutableMap.Builder<>();

    // 双重校验,避免一个命名空间有多个本地备份
    //1. 使用getProperty获取原始值
    for (ConfigChange change : configChanges) {
      change.setOldValue(this.getProperty(change.getPropertyName(), change.getOldValue()));
    }

    //2. 更新PropertySource   !!!划重点,值在这里进行更新
    updateConfig(newConfigProperties, sourceType);
    clearConfigCache();

    //3. use getProperty to update configChange's new value and calc the final changes
    for (ConfigChange change : configChanges) {
      change.setNewValue(this.getProperty(change.getPropertyName(), change.getNewValue()));
      switch (change.getChangeType()) {
        case ADDED:
         // .....
          actualChanges.put(change.getPropertyName(), change);
          break;
        case MODIFIED:
          if (!Objects.equals(change.getOldValue(), change.getNewValue())) {
            actualChanges.put(change.getPropertyName(), change);
          }
          break;
        case DELETED:
            // .....
          actualChanges.put(change.getPropertyName(), change);
          break;
        default:
          //do nothing
          break;
      }
    }
    return actualChanges.build();
  }
  1. DefaultConfig.updateConfig, 替换本地缓存,用于后续读取
private final AtomicReference<Properties> m_configProperties;

private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
    m_configProperties.set(newConfigProperties);
    m_sourceType = sourceType;
}

# 更新bean

# AutoUpdateConfigChangeListener

  1. 初始化
    • 判断是否允许spring property自动更新,如果不允许,则不初始化AutoUpdateConfigChangeListener
    • 初始化AutoUpdateConfigChangeListener
    • 给所有PropertySource的listeners添加AutoUpdateConfigChangeListener,响应变更
public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {
    // ...

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    /// ....
    initializeAutoUpdatePropertiesFeature(beanFactory);
  }
  
  private void initializeAutoUpdatePropertiesFeature(ConfigurableListableBeanFactory beanFactory) {
      // 如果关闭Spring property的自动更新,不添加监听器,响应更新
      // 默认开启,通过设置参数apollo.autoUpdateInjectedSpringProperties=false关闭自动更新
      if (!configUtil.isAutoUpdateInjectedSpringPropertiesEnabled() ||
        !AUTO_UPDATE_INITIALIZED_BEAN_FACTORIES.add(beanFactory)) {
      return;
    }

    // 初始化AutoUpdateConfigChangeListener
    AutoUpdateConfigChangeListener autoUpdateConfigChangeListener = new AutoUpdateConfigChangeListener(
        environment, beanFactory);

    // 读取所有的PropertySource, 添加AutoUpdateConfigChangeListener,响应变更
    List<ConfigPropertySource> configPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
    for (ConfigPropertySource configPropertySource : configPropertySources) {
      configPropertySource.addChangeListener(autoUpdateConfigChangeListener);
    }
}
  1. 响应变更
public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
  // .....

  /**
   * 响应变更
   * @param changeEvent bean 对象,直接调用,并非事件响应模式
   */
  @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. 遍历更新bean对象的值
      for (SpringValue val : targetValues) {
        updateSpringValue(val);
      }
    }
  }

  private void updateSpringValue(SpringValue springValue) {
    try {
        // 从propertySource中读取新值
      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) {
    // 通过placeholder读取
    Object value = placeholderHelper
            .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder());

    // 将读取到到value转换为指定的类型
    // 。。。。
    return value;
  }
  // ....
}
  • PlaceholderHelper.resolvePropertyValue
public class PlaceholderHelper {

  /**
   * Resolve placeholder property values, e.g.
   * 将"${somePropertyValue}"替换成对应的值:"the actual property value"
   */
  public Object resolvePropertyValue(ConfigurableBeanFactory beanFactory, String beanName, String placeholder) {
    // resolve string value
    String strVal = beanFactory.resolveEmbeddedValue(placeholder);

    /// ...
  }
}
  • AbstractBeanFactory
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    
    @Nullable
    public String resolveEmbeddedValue(@Nullable String value) {
        if (value == null) {
            return null;
        } else {
            String result = value;
            Iterator var3 = this.embeddedValueResolvers.iterator();

            do {
                if (!var3.hasNext()) {
                    return result;
                }

                StringValueResolver resolver = (StringValueResolver)var3.next();
                result = resolver.resolveStringValue(result);
            } while(result != null);

            return null;
        }
    }
}

  • debug到这里,会发现代码跟不下去了,先看看debug的内容 从图中我们可以看到,resolver指向是PropertySourcesPlaceholderConfigurer类,但可点进去的实现类,并没有这个类,接下来我们先来找找这个实现类 update update2

我们的思路是,首先去看apollo-client的starter是怎么加载的

  • 找到apollo-client的spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
  • ApolloAutoConfiguration
@Configuration
@ConditionalOnProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED)
@ConditionalOnMissingBean(PropertySourcesProcessor.class)
public class ApolloAutoConfiguration {

  @Bean
  public ConfigPropertySourcesProcessor configPropertySourcesProcessor() {
    return new ConfigPropertySourcesProcessor();
  }
}
  • ConfigPropertySourcesProcessor

在启动的时候,将PropertySourcesPlaceholderConfigurer注册为bean定义,并交由spring注册

public class ConfigPropertySourcesProcessor extends PropertySourcesProcessor
    implements BeanDefinitionRegistryPostProcessor {

  @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
    // to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
    propertySourcesPlaceholderPropertyValues.put("order", 0);

    // 我们需要找的PropertySourcesPlaceholderConfigurer
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
        PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
    /// .....
    processSpringValueDefinition(registry);
  }
}

  • PropertySourcesPlaceholderConfigurer.StringValueResolver

回到之前跟不下去的AbstractBeanFactory,我们在PropertySourcesPlaceholderConfigurer找到StringValueResolver方法

public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
  protected boolean ignoreUnresolvablePlaceholders = false;

  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    /// ...
    this.processProperties(beanFactory, (ConfigurablePropertyResolver)(new PropertySourcesPropertyResolver(this.propertySources)));
    this.appliedPropertySources = this.propertySources;
  }

  protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
          ConfigurablePropertyResolver propertyResolver) throws BeansException {
    propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
    propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
    propertyResolver.setValueSeparator(this.valueSeparator);
    StringValueResolver valueResolver = (strVal) -> {
      String resolved = this.ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal)
                                                            : propertyResolver.resolveRequiredPlaceholders(strVal);
      if (this.trimValues) {
        resolved = resolved.trim();
      }

      return resolved.equals(this.nullValue) ? null : resolved;
    };
    this.doProcessProperties(beanFactoryToProcess, valueResolver);
  }
}

在这里打个断点,同时更新apollo的值,发现代码果然走到了这里 update3

从代码可以看到使用propertyResolver.resolveRequiredPlaceholders(strVal)处理参数值读取,而propertyResolver来自于上一次所创建的PropertySourcesPropertyResolver

  • 找到PropertySourcesPropertyResolver继承自父类AbstractPropertyResolver的resolveRequiredPlaceholders方法
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {

  public String resolveRequiredPlaceholders(String text) {
    if (this.strictHelper == null) {
      this.strictHelper = this.createPlaceholderHelper(false);
    }

    return this.doResolvePlaceholders(text, this.strictHelper);
  }
  
  private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
    return helper.replacePlaceholders(text, this::getPropertyAsRawString);
  }
}

  • PropertySourcesPropertyResolver.getPropertyAsRawString

helper.replacePlaceholders可以理解为传了一个方法进去,类似于使用el表达式的方式进行调用, 所以真正取值的地方在getPropertyAsRawString中。 PropertySourcesPropertyResolver中的propertySources,在创建的时候传入值: 系统初始化时读取到的所有的propertySource。

这里的核心就是,遍历所有的PropertySource, key匹配即为取到对应值。这里存在一个问题,如果一个key在多个命名空间中被设置,那么会被后加载的命名空间进行覆盖。

当然,这种使用方式也是不对的,应该确保在使用多个命名空间的时候,一个key只在一个命名空间中被定义。

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
  // ...
  @Nullable
  private final PropertySources propertySources;

  public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
    this.propertySources = propertySources;
  }
  
  @Nullable
  protected String getPropertyAsRawString(String key) {
    return (String)this.getProperty(key, String.class, false);
  }

  @Nullable
  protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        // 遍历propertySources
      Iterator var4 = this.propertySources.iterator();

      while (var4.hasNext()) {
        PropertySource<?> propertySource = (PropertySource)var4.next();
        if (this.logger.isTraceEnabled()) {
          this.logger.trace("Searching for key '" + key + "' in PropertySource '" + propertySource.getName() + "'");
        }

        // 从当前的propertySource中获取值,如果取到,就设置在value中
        Object value = propertySource.getProperty(key);
        if (value != null) {
          if (resolveNestedPlaceholders && value instanceof String) {
            value = this.resolveNestedPlaceholders((String)value);
          }

          // 打印参数被找到的日志
          this.logKeyFound(key, propertySource, value);
          // 转换value到目标类型
          return this.convertValueIfNecessary(value, targetValueType);
        }
      }
    }

    if (this.logger.isTraceEnabled()) {
      this.logger.trace("Could not find key '" + key + "' in any property source");
    }

    return null;
  }
  
}
Last Updated: 6/26/2022, 4:44:06 PM