在一些场景下需要在应用启动或者运行过程中,动态的修改.properties或者是.yaml中的某个属性值。如果有使用过类似携程的Apollo配置中心应该会有所体会。

1. 分析

Springboot 的启动过程中,将所有的应用参数等环境变量属性值都解析到类 ConfigurableEnvironment 中,而对应的 .properties .yaml 等配置文件中的属性值就保存在改类的 ConfigurablePropertyResolver PropertySources 中,它是一个迭代器,每一个配置资源文件都会解析为一个对象。 如下图所示,这几个 PropertySources 是我的应用启动后解析的配置源信息。并且红框标注为我自定义的配置文件( application.yaml )。

ConfigurableEnvironment#getProperty 获取对应的属性值,最终会进入到 PropertySourcesPropertyResolver#getProperty 中,从这里可以看出,它遍历了这个迭代器,然后通过 key 获取对应的值,当获取到对应的值后就直接返回。这里做个假设,比如某个属性值 source.url 这个 key 在这个迭代器的下标为8和9这两个资源文件中都存在,那么从以上结论中可以得出,这里是获取到了下标为8的资源文件的对应值。

@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
    if (this.propertySources != null) {
        for (PropertySource<?> propertySource : this.propertySources) {
            if (logger.isTraceEnabled()) {
                logger.trace("Searching for key '" + key + "' in PropertySource '" +
                        propertySource.getName() + "'");
            Object value = propertySource.getProperty(key);
            if (value != null) {
                if (resolveNestedPlaceholders && value instanceof String) {
                    value = resolveNestedPlaceholders((String) value);
                logKeyFound(key, propertySource, value);
                return convertValueIfNecessary(value, targetValueType);
    if (logger.isTraceEnabled()) {
        logger.trace("Could not find key '" + key + "' in any property source");
    return null;

从上述分析的结论中,可以得出,如果在项目的允许过程中,想要修改某个key对应的参数值,那么仅需要将修改后的值伪装成PropertySource,添加到这个PropertySources迭代器的头部,那么通过这段代码获取到的值,就是修改后的值。实际上apollo也是通过这种方式实现资源配置项动态更新,当然它的实现更加复杂一点,具体请参考文末链接。

想要了解org.springframework.core.env.PropertySource类的话,搜索一下PropertySource类即可。这里使用和读取配置文件一样的类对象OriginTrackedMapPropertySource,将它添加到propertySources的头部。

@Service
public class TestServer implements BeanFactoryPostProcessor, EnvironmentAware {
    private ConfigurableEnvironment environment;
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = (ConfigurableEnvironment) environment;;
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        MutablePropertySources sources = environment.getPropertySources();
        String PROP_SOURCE_NAME = "custom_property";
        Map<String, Object> mapProp = new HashMap<>(1);
        mapProp.put("order.new", "http://xxx");
        OriginTrackedMapPropertySource source = new OriginTrackedMapPropertySource(PROP_SOURCE_NAME, mapProp);
        sources.addFirst(source);

动态修改properties配置项值
Apollo配置中心设计