PropertySourcesPlaceholderConfigurer 是 Spring 框架中用于解析属性占位符 (如 ${property.name}) 的类, 它通过读取配置文件 (如 .properties 文件) 并将这些属性值注入到 Spring 的 BeanDefinition 中;
PropertySourcesPlaceholderConfigurer 代替了之前的 PropertyPlaceholderConfigurer,
继承关系
1 | BeanFactoryPostProcessor |
加载时机
springboot 场景
当应用标记了 @EnableAutoConfiguration, springboot 便会激活 AutoConfigurationImportSelector, 通过 SpringFactoriesLoader 扫描到与占位符替换相关的 PropertyPlaceholderAutoConfiguration, 从而自动注入 PropertySourcesPlaceholderConfigurer 类:1
2
3
4
5
6
7
8
9
10
11false) (proxyBeanMethods =
(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {
(search = SearchStrategy.CURRENT)
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
PropertySourcesPlaceholderConfigurer 实现了 EnvironmentAware, 其能够感知到 Environment, 并最终获取所有 properties 的真实值: 通过 environment.getPropertySources() 拿到 propertySources:1
2
3
4
5
6
7
8public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware {
private PropertySources propertySources;
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
14public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener {
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
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 字段
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
15PropertyValues 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;
}