Open liuxi1211 opened 3 weeks ago
问题描述: 当使用 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; } }
问题描述: 当使用 SpringCloud 并且未主动关闭 BootstrapApplicationListener 时,如果动态调整配置,新增和删除 key 无法正确映射到绑定属性上
问题原因: SpringCloud 项目启动时 BootstrapApplicationListener 会在 applicationContext 创建之前先创建一个 bootstrapApplicationContext,用来优先加载环境变量,这会出现两次容器加载,触发两次 ApolloApplicationContextInitializer 初始化 当触发两次 ApolloApplicationContextInitializer 初始化时 CachedCompositePropertySource 缓存的 propertyNames 会因监听器问题无法刷新
代码说明: