PropertyPlaceholderConfigurer的作用
这个类的作用是把我们配置文件中或者代码中的${}占位符替换为properties文件中的配置
使用方式如下
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
<property name="location" value="classpath:test.properties" />
</bean>
只需要指定配置文件的路径即可,会自动把ioc容器中的占位符替换为对应配置
原理
PropertyPlaceholderConfigurer主要是使用到了Spring的BeanFactoryPostProcessor的postProcessBeanFactory回调。
这个回调方法会在BeanFactory初始化并且加载好BeanDefinition后执行,但会在所有Bean初始化之前。PropertyPlaceholderConfigurer中回调的主要逻辑就是遍历BeanDefinition,对占位符进行替换。
我们先看下PropertyPlaceholderConfigurer的类继承图
可以看到PropertyResourceConfigurer继承了BeanFactoryPostProcessor接口,我们从它的postProcessBeanFactory方法看起
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
这边有三个步骤,mergeProperties方法merge操作基本没有,主要是用来从硬盘加载properties文件。
convertProperties用来对PropertyValue做一些自定义转换,默认是返回原值,所以这个方法忽略即可。
processProperties是具体的propeties替换逻辑,交给子类实现,PropertyPlaceholderConfigurer的实现如下。
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
}
PlaceholderResolvingStringValueResolver封装了通过占位符从配置文件获取对应配置的逻辑,我们基本可以认为{a.b}可以更复杂,比如{a.b}.${c.d}这种复杂的情况。
遍历BeanDefinition对占位符进行替换的逻辑则封装在doProcessProperties方法中,这个实现在父类PlaceholderConfigurerSupport中实现
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 {
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
基本逻辑和描述的差不多,BeanDefinitionVisitor封装了操作BeanDefiniton逻辑,对占位符进行替换
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
可以看到占位符可以出现在以上这些地方。不过我们一般使用在PropertyValue中。
总结
其实配置文件替换bean的属性的原理挺简单,就是通过BeanFactoryPostProcessor在bean初始化之前,加载Beanfinition之后对Beanfinition中的占位符进行替换。
分析这个类的主要目的是,因为Disconf动态配置,就是在这个类的基础上修改实现的。
网友评论