Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpringCloud 环境 @ConfigurationProperties 无法监听到新增和删除 key 问题 #88

Open
liuxi1211 opened this issue Oct 29, 2024 · 1 comment
Labels
help wanted Extra attention is needed need investigation

Comments

@liuxi1211
Copy link

问题描述:
当使用 SpringCloud 并且未主动关闭 BootstrapApplicationListener 时,如果动态调整配置,新增和删除 key 无法正确映射到绑定属性上

问题原因:
SpringCloud 项目启动时 BootstrapApplicationListener 会在 applicationContext 创建之前先创建一个 bootstrapApplicationContext,用来优先加载环境变量,这会出现两次容器加载,触发两次 ApolloApplicationContextInitializer 初始化
当触发两次 ApolloApplicationContextInitializer 初始化时 CachedCompositePropertySource 缓存的 propertyNames 会因监听器问题无法刷新

代码说明:

   // ApolloApplicationContextInitializer::initialize

   CompositePropertySource composite;
    if (configUtil.isPropertyNamesCacheEnabled()) {

     // 此处每次都会创建一个新的 CachedCompositePropertySource ,但是 name 相同
      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));
    }
 // AbstractConfig::addChangeListener
 @Override
  public void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys, Set<String> interestedKeyPrefixes) {

    /* 此处根据 PropertySource 名称判断,由于 bootstrapApplicationContext 中已经设置过一次
     * applicationContext 创建时新 new 对象 CachedCompositePropertySource 无法设置到监听里面
     * 进而导致监听器中的保存的一直是旧的 CachedCompositePropertySource,而 applicationContext 实际使用新的
     */
    if (!m_listeners.contains(listener)) {
      m_listeners.add(listener);
      if (interestedKeys != null && !interestedKeys.isEmpty()) {
        m_interestedKeys.put(listener, Sets.newHashSet(interestedKeys));
      }
      if (interestedKeyPrefixes != null && !interestedKeyPrefixes.isEmpty()) {
        m_interestedKeyPrefixes.put(listener, Sets.newHashSet(interestedKeyPrefixes));
      }
    }
  }
public class CachedCompositePropertySource extends CompositePropertySource implements
    ConfigChangeListener {

  private volatile String[] names;

  public CachedCompositePropertySource(String name) {
    super(name);
  }

  @Override
  public String[] getPropertyNames() {
   // 无法触发监听器,缓存仍然是所有旧 key,而 SpringBoot 属性绑定会使用这个方法
    String[] propertyNames = this.names;
    if (propertyNames == null) {
      this.names = propertyNames = super.getPropertyNames();
    }
    return propertyNames;
  }

  @Override
  public void onChange(ConfigChangeEvent changeEvent) {

    // 正常情况下应该触发监听器,让 names 置空,从而触发调用时刷新
   // 此处 applicationContext 中 CachedCompositePropertySource 无法触发
    this.names = null;
  }
}
@nobodyiam nobodyiam added help wanted Extra attention is needed need investigation labels Nov 2, 2024
@nobodyiam
Copy link
Member

  1. 是否能上传一个最简 demo 复现该问题?
  2. 对于该问题是否有修复建议?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed need investigation
Projects
None yet
Development

No branches or pull requests

2 participants