You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重写PropertySourcesPlaceholderConfigurer后RefreshScope失效问题排查

问题原因分析

你写的PasswordUpdatePropertyConfigurer继承了PropertySourcesPlaceholderConfigurer,这个类是BeanFactoryPostProcessor,它的loadProperties方法只会在Spring容器启动初期执行一次——把处理后的属性存入内部的Properties对象,后续所有依赖这些属性创建的Bean,都是从这个缓存的Properties里取值。

而Spring Cloud的/env和/refresh端点,本质是更新Environment中的属性源,但不会触发PropertySourcesPlaceholderConfigurer重新执行loadProperties方法。这就导致:更新后的属性不会被你的转换逻辑处理,而且Bean依然使用容器启动时缓存的旧属性值,自然看不到同步更新的效果。

正确解决方案

不要用PropertySourcesPlaceholderConfigurer来做属性转换,改用包装PropertySource的方式,让属性读取的整个链路都经过你的转换逻辑,包括/refresh之后的属性更新。

可以实现EnvironmentPostProcessor,在环境初始化时把原有的PropertySource替换为自定义包装类:

@Component
@Slf4j
public class PasswordUpdateEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        List<PropertySource<?>> sourcesToWrap = new ArrayList<>();
        
        // 筛选需要包装的可枚举PropertySource,避免重复包装
        for (PropertySource<?> source : propertySources) {
            if (source instanceof EnumerablePropertySource && !(source instanceof WrappedPasswordPropertySource)) {
                sourcesToWrap.add(source);
            }
        }
        
        // 替换原PropertySource为包装后的版本
        for (PropertySource<?> source : sourcesToWrap) {
            propertySources.replace(source.getName(), new WrappedPasswordPropertySource(source));
        }
    }

    // 自定义包装类,对属性读取做转换
    static class WrappedPasswordPropertySource extends EnumerablePropertySource<Object> {
        private final EnumerablePropertySource<?> delegate;

        public WrappedPasswordPropertySource(PropertySource<?> delegate) {
            super(delegate.getName(), delegate.getSource());
            this.delegate = (EnumerablePropertySource<?>) delegate;
        }

        @Override
        public Object getProperty(String name) {
            Object value = delegate.getProperty(name);
            // 执行你的属性转换逻辑
            if (value instanceof String strValue && StringUtils.isNotEmpty(strValue) && strValue.startsWith("password")) {
                return strValue.replace("password", "niti");
            }
            return value;
        }

        @Override
        public String[] getPropertyNames() {
            return delegate.getPropertyNames();
        }
    }
}
方案优势
  • 无论容器启动还是/refresh更新属性后,只要读取属性就会经过WrappedPasswordPropertySource的getProperty方法,自动应用转换逻辑;
  • 如果Bean使用了@RefreshScope注解,刷新后会重新读取Environment中的属性,自然能拿到转换后的新值;
  • 完全兼容Spring Cloud的配置刷新机制,不会出现属性不同步的问题。

内容的提问来源于stack exchange,提问作者Nitika Bansal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:30:18