在一些场景下需要在应用启动或者运行过程中,动态的修改.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);
}
}