PropertySourcesPlaceholderConfigurer 是 Spring 框架中用于解析属性占位符 (如 ${property.name}) 的类, 它通过读取配置文件 (如 .properties 文件) 并将这些属性值注入到 Spring 的 BeanDefinition 中;
PropertySourcesPlaceholderConfigurer 代替了之前的 PropertyPlaceholderConfigurer,

继承关系

1
2
3
4
5
6
7
8
9
10
BeanFactoryPostProcessor
^
|
PropertyResourceConfigurer
^
|
PlaceholderConfigurerSupport
^
|
PropertySourcesPlaceholderConfigurer

加载时机

springboot 场景

当应用标记了 @EnableAutoConfiguration, springboot 便会激活 AutoConfigurationImportSelector, 通过 SpringFactoriesLoader 扫描到与占位符替换相关的 PropertyPlaceholderAutoConfiguration, 从而自动注入 PropertySourcesPlaceholderConfigurer 类:

1
2
3
4
5
6
7
8
9
10
11
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {

@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

}

PropertySourcesPlaceholderConfigurer 实现了 EnvironmentAware, 其能够感知到 Environment, 并最终获取所有 properties 的真实值: 通过 environment.getPropertySources() 拿到 propertySources:

1
2
3
4
5
6
7
8
public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
private PropertySources propertySources;

@Override
public void setEnvironment(Environment environment) {
this.propertySources = ((ConfigurableEnvironment) environment).getPropertySources();
}
}

而 Environment 是基于 EnvironmentPostProcessor 通过 resourceLoader 读取所有 properties 的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener {

@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
// 加载 application.properties 或 application.yml 文件
addPropertySources(environment, application.getResourceLoader());
}

private void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
// 加载默认的 application.properties 文件
PropertySource<?> propertySource = new ResourcePropertySource("applicationProperties", resourceLoader.getResource("classpath:application.properties"));
environment.getPropertySources().addLast(propertySource);
}
}

传统 xml 场景

1
PropertyPlaceholderBeanDefinitionParser -> AbstractPropertyLoadingBeanDefinitionParser -> AbstractSingleBeanDefinitionParser -> AbstractBeanDefinitionParser -> BeanDefinitionParser

执行过程

PropertySourcesPlaceholderConfigurer 需要在 bean 被实例化之前执行, 也就是说它操纵的不是 spring bean, 而是 BeanDefinition; 如果 PropertySourcesPlaceholderConfigurer 不在 bean 实例化之前处理 BeanDefinition, 那么在 bean 实例化时属性占位符将无法被正确解析, 导致 bean 的属性值不正确;
为了在 Spring 容器实例化 bean 之前完成这些操作,PropertySourcesPlaceholderConfigurer 实现了 BeanFactoryPostProcessor 接口:

  • BeanFactoryPostProcessor 允许在 spring 容器实例化 bean 之前对 BeanDefinition 进行修改;
    • 在 spring 容器加载 BeanDefinition 之后 & 实例化 bean 之前, BeanFactoryPostProcessor 的 postProcessBeanFactory 方法会被调用;
  • PropertyPlaceholderConfigurer 实现了这个方法, 其会遍历所有的 BeanDefinition, 查找其中的属性占位符 (如 ${property.name}), 并从配置文件中读取相应的属性值, 替换这些占位符;

 
简要过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// BeanFactoryPostProcessor#postProcessBeanFactory
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
......
processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
......
}

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
......
doProcessProperties(beanFactoryToProcess, valueResolver);
}

在 PlaceholderConfigurerSupport#doProcessProperties 方法中, 会对注册到 beanFactory 中的每一个 beanDefinition 执行扫描:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
// 扫描 beanDefinition 并替换
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
......
}

在 BeanDefinitionVisitor#visitBeanDefinition 方法里, 会对目标 beanDefinition 做一次全身大体检, 所有的 bd 属性均要 resolve 一遍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}

  • 对于 String 类型的属性使用 resolveStringValue 方法解析占位符, 比如: parentClassName、factoryBeanName、scope 等;
  • 对于非字符串类型的属性使用 resolveValue 方法解析占位符, 比如: propertyValues、constructorArgumentValues 等;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
       // visitBeanDefinition 方法片段
    if (beanDefinition.hasPropertyValues()) {
    visitPropertyValues(beanDefinition.getPropertyValues());
    }

    protected void visitPropertyValues(MutablePropertyValues pvs) {
    PropertyValue[] pvArray = pvs.getPropertyValues();
    for (PropertyValue pv : pvArray) {
    Object newVal = resolveValue(pv.getValue());
    if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
    pvs.add(pv.getName(), newVal);
    }
    }
    }

职责划分

PropertySourcesPlaceholderConfigurer 只负责将占位符解析后填充到 beanDefinition, 而不负责填充到最终 spring bean 的这 “最后一公里”; 将 spring bean 中的占位符最终替换为真实值的逻辑其实另有其人:

  • @Value: 通过 AutowiredAnnotationBeanPostProcessor 实现注入:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
       // pvs 是 beanDefinition 的 propertyValues 字段
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
    metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
    throw ex;
    }
    catch (Throwable ex) {
    throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
    }

    AutowiredAnnotationBeanPostProcessor 类实现了 SmartInstantiationAwareBeanPostProcessor 接口, 在 AbstractAutowireCapableBeanFactory#populateBean 方法中, 会将所有已注册的 InstantiationAwareBeanPostProcessor 执行一遍, 从而完成对占位符的增强替换:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
       PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    if (pvsToUse == null) {
    if (filteredPds == null) {
    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
    if (pvsToUse == null) {
    return;
    }
    }
    pvs = pvsToUse;
    }