本文引自我的个人博客: sunmingshuai.coding.me
这篇文章我们介绍一个常用的BFPP
来消除我们心中的对BFPP
的陌生感
在spring
配置文件中 读者一定经常遇到过${xxx}
的写法 然后spring
会帮我们替换成properties
中对应的value
值 那么这个变量是什么时候又是被哪个类替换的呢
在文章ClassPathXmlApplicationContext源码解析三:BFPP中我们提到了BFPP
的概念 BFPP
的全称是BeanFactoryPostProcessor
是工厂级别的处理器 它的调用时间点是在将配置文件转化为BeanDefinition
之后 加载成bean
之前调用的
我们先看下配置文件
<bean id="dev_propertyPlace" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="2"/>
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="locations">
<list>
<value>classpath:config-sunms-dev.properties</value>
</list>
</property>
</bean>
在使用这个bfpp
之前当然是先获取这个bean
处理一些事情 比如将locations
对应的string
转化成需要的resource
类型
既然是bfpp
那么我们的阅读入口就好找了 PropertyResourceConfigurer##postProcessBeanFactory()
方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
//加载properties文件
Properties mergedProps = mergeProperties();
convertProperties(mergedProps);
//替换
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
spring
的代码一直这么优雅 高度抽象 让人一看就知道要做什么事情
第一步就是加载我们指定的properties
文件 第二步默认没有做什么事情
最后一步就是重点了
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
}
先是构建一个处理类 处理类不过是提供一些方法 定义了诸如前缀${
后缀}
使后面的代码更简洁 然后交给doProcessProperties
处理
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.
// 处理如@Value("${name}")
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
下面的代码就比较简单了 就是处理所有的BeanDefinitioin
不包含本身 将诸如parentName
等属性中的${xxx}
变量替换掉
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());
}
有趣的是最后一行代码
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
这句代码把构建好的工具类加入到了beanFactory
中 是spring3.0
加入的 加入的作用就是处理诸如@Value
注解的时候 如果有${xxx}
的形式的话 会被这个工具类给替换掉 这个在@Autowired注解工作原理源码解析的时候 我们会再提到