在做项目中,我们经常会把项目使用到的常量放到一个或者多个Properties文件中;项目启动加载Properties文件,程序会动态的注入到相应的属性上或者动态从Properties中获取value。
如果使用Spring框架搭建的项目,Spring是如何处理Properties的?
通过学习Spring和翻阅Spring源码我了解到Spring对Properties的处理方式;接下来我会把学习的和从源码看到的详细介绍一下。
Configurer Properties
PropertyPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer
Spring 对于Properties的操作都是分别基于上面两个类,而且两个类的实现方式是不一样的。
PropertyPlaceholderConfigurer
这个类型是我在接触Spring框架的时候就使用的类;但是当时在用的时候没有过多关注该类的实现方式,只停留在使用上;之后由于项目的功能需要我了解它的实现方式,并且写了一篇文章专门介绍了该类。
有兴趣的可以先看看这篇文章:Spring Properties Loader
总结一下该类:
加载
- 指定的properties文件
- 合并指定的properties (参考:
PropertiesLoaderSupport.localProperties
)
解析
- props (上面两种加载后的合并properties)
- System.getProperty()
- System.getenv()
具体如何查找值,请参考源码;
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#resolvePlaceholder(String, Properties, int)
PropertySourcesPlaceholderConfigurer
首先看看类结构:
通过对比两个类的结构发现,除了类名的区别,还有就是PropertySourcesPlaceholderConfigurer
类实现EnvironmentAware
接口,那么证明该类会有Environment
的操作;
了解Spring的知道,如果是非web环境,接口Environment
的实例类是StandardEnvironment
;web环境,接口Environment
的实例类是StandardServletEnvironment
;而且实现类会初始化系统属性System.getProperties
和环境变量System.getenv
到PropertySources中。
getSystemProperties()
和getSystemEnvironment()
两个方法的内容我就不粘出来了,自行去参考源码;
不同
两个类加载的属性有什么不同呢?
PropertyPlaceholderConfigurer
这个类是把所有的属性集中放到Properties中;
PropertySourcesPlaceholderConfigurer
该类有一个
PropertySources
的集合,集合中放的是PropertySource
,它是一个抽象类,getProperty
方法交由子类实现;每一个PropertySource
可以是一个Properties,而且PropertySource
可以是任何东西;例如:System.getProperteis
、System.getenv
而该类直接重写了
postProcessBeanFactory
方法,如果PropertySources
集合为空,此类会把Environment
、Properties文件
、localProperties
放到集合中;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
this.propertySources.addLast(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
);
}
try {
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
}
else {
this.propertySources.addLast(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}
取值
PropertyPlaceholderConfigurer
Properteis、System.getenv、System.getProperties
PropertySourcesPlaceholderConfigurer
因为此类中汇聚了
Environment
、多个PropertySource
;因为Environment中加载了System.getProperteis
和System.getenv
,而且它们分别代表了一个PropertySource;每个PropertySource会放到一个PropertySources集合中,每次取值时,会根据位置顺序,从每一个PropertySource中获取属性值;参考:StandardEnvironment#customPropertySources()
上面源码中,我们看到此类把
Environment
作为一个PropertySource,放到PropertySources集合中;而Environment
中也有一个PropertySources集合,此集合中默认初始化的是System.getProperteis
和System.getenv
;
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
@Override
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
@Override
public String getProperty(String key, String defaultValue) {
return this.propertyResolver.getProperty(key, defaultValue);
}
@Override
public <T> T getProperty(String key, Class<T> targetType) {
return this.propertyResolver.getProperty(key, targetType);
}
@Override
public <T> T getProperty(String key, Class<T> targetType, T defaultValue) {
return this.propertyResolver.getProperty(key, targetType, defaultValue);
}
@Override
@Deprecated
public <T> Class<T> getPropertyAsClass(String key, Class<T> targetType) {
return this.propertyResolver.getPropertyAsClass(key, targetType);
}
@Override
public String getRequiredProperty(String key) throws IllegalStateException {
return this.propertyResolver.getRequiredProperty(key);
}
@Override
public <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException {
return this.propertyResolver.getRequiredProperty(key, targetType);
}
@Override
public String resolvePlaceholders(String text) {
return this.propertyResolver.resolvePlaceholders(text);
}
@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
综合我们讲的和源码,我们可以知道;此类汇聚了各种不用的PropertySource;
-
props
把properties文件、localProperties封装成PropertySource;
-
Environment
有自己的PropertySources;