概述
<context:property-placeholder>的作用是向Spring容器中注入一个属性占位解析器,用来处理BeanDefinition中的各种占位符,用配置文件的信息替换占位符。这是个自定义标签,Spring会使用PropertyPlaceholderBeanDefinitionParser解析它。
这个标签可以定义各种属性
- location:属性文件路径,符合Spring的Resource定义
- properties-ref:指定Properties对象的bean
- file-encoding
- ignore-resource-not-found:默认false,表示属性文件找不到就报错
- ignore-unresolvable:默认false,表示属性找不到就报错
- local-override:默认false,如果localOverride=false,配置的优先级:environment > location > property-ref,如果localOverride=true,配置的优先级:environment < location < property-ref
- system-properties-mode:ENVIRONMENT、NEVER、FALLBACK、OVERRIDE,默认是ENVIRONMENT
- value-separator,间隔符,默认 ':'
- trim-values,默认false,true表示解析占位符的时候,执行trim操作去除两端的空格
- null-value,定义一个null值判断,符合这个值的就表示应该返回null
PS:localOverride的运用,如果localOverride=false,配置的优先级:environment > location > property-ref,如果localOverride=true,配置的优先级:environment < location < property-ref
我从源码进行分析,仔细看下这些属性是怎么运用的。
<context:property-placeholder>的解析
这是个自定义标签,Spring会使用PropertyPlaceholderBeanDefinitionParser解析它。
@Override
protected Class<?> getBeanClass(Element element) {
// system-properties-mode=ENVIRONMENT
if (element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIB).equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {
return PropertySourcesPlaceholderConfigurer.class;
}
return PropertyPlaceholderConfigurer.class;
}
说明:
- 如果system-properties-mode=ENVIRONMENT就注册类型为PropertySourcesPlaceholderConfigurer的Bean,否则为PropertyPlaceholderConfigurer
- PropertySourcesPlaceholderConfigurer与PropertyPlaceholderConfigurer相比,就是增加environment中的属性
PropertySourcesPlaceholderConfigurer
将所有的属性,包括environment、location、properties-ref,组装到PropertySources对象中。然后使用PropertySources去获取需要替换属性的真实值,依次迭代PropertySource,返回第一个找到的值。PropertySources是PropertySource的数据结构,里面封装多个PropertySource对象。
解析占位符过程
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
// 1、准备PropertySources属性
...
}
// 2、解析占位符
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
this.appliedPropertySources = this.propertySources;
}
1、准备PropertySources属性
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
// 加入environment的PropertySource,名称为environmentProperties
this.propertySources.addLast(
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
public String getProperty(String key) {
// 从environment中获取属性
return this.source.getProperty(key);
}
}
);
}
try {
// 加入配置文件的的PropertySource,名称为localProperties
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
// 如果localOverride=true,就优先使用localProperties里的属性。做法是把localProperties的属性放在environmentProperties的前面
if (this.localOverride) {
this.propertySources.addFirst(localPropertySource);
}
else {
this.propertySources.addLast(localPropertySource);
}
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
说明:
- 所有属性放入PropertySources对象中
- environment的PropertySource,名称为environmentProperties
- 配置文件的的PropertySource,名称为localProperties
PS:localOverride的运用,如果localOverride=false,配置的优先级:environment > location > property-ref,如果localOverride=true,配置的优先级:environment < location < property-ref
1.1、mergeProperties()
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
if (this.localOverride) {
// 先加载location定义的文件,会被property-ref中的属性覆盖
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
if (this.localProperties != null) {
// 加载bean中定义的property对象
for (Properties localProp : this.localProperties) {
CollectionUtils.mergePropertiesIntoMap(localProp, result);
}
}
if (!this.localOverride) {
// 后加载location定义的文件,会覆盖property-ref中的属性
// Load properties from file afterwards, to let those properties override.
loadProperties(result);
}
return result;
}
2、解析占位符
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
propertyResolver.setValueSeparator(this.valueSeparator);
StringValueResolver valueResolver = strVal -> {
String resolved = (ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
};
doProcessProperties(beanFactoryToProcess, valueResolver);
}
说明:
- ignoreUnresolvablePlaceholders=true,表示找不到不报错。resolveRequiredPlaceholders方法找不到就抛错
- trimValues=true,会使用trim去除空格
- nullValue表示的一个null值的表现
- 这个propertyResolver是上个方法创建的PropertySourcesPropertyResolver对象
3、PropertySourcesPropertyResolver
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.isDebugEnabled()) {
logger.debug("Could not find key '" + key + "' in any property source");
}
return null;
}