1:写在前面
在这篇文章中我们分析了BeanFactoryPostProcessor,知道了,其也是spring提供给我们的一个扩展点,这个点
是在外部配置生成BeanDefinition对象之后,在通过BeanDefinition创建spring bean之前,允许我们对BeanDefinition进行自己的定制化处理,本文要分析PropertyOverrideConfigurer就是BeanFacotryPostProcessor的一个子类,利用其实现了动态替换bean的属性值的功能,工程上用的不多,但是也有必要了解下。其和PropertyPlaceholderConfiguer(重要!!!)功能类似,感兴趣的也可以看下。
2:功能说明
该类允许我们按照{beanName}.{propName}={要替换的val}
的格式在配置文件中定义信息,表达的含义就是替换bean名称为{beanName}的bean的属性{propName}的值为{要替换的val}
。
3:例子
3.1:测试的bean
public class MyPropertyOverrideConfigurerBean {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
}
3.2:xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer"
id="propertyOverrideConfigurer">
<property name="locations">
<list>
<value>classpath:propertyoverrideconfigurertest/propertyoverrideconfigurertest.properties</value>
</list>
</property>
<property name="fileEncoding" value="GBK"/>
</bean>
<bean class="yudaosourcecode.studyhelp.MyPropertyOverrideConfigurerBean"
id="myPropertyOverrideConfigurerBean">
<property name="name" value="张三"/>
</bean>
</beans>
3.3:配置文件
myPropertyOverrideConfigurerBean.name=李四
3.4:测试
@Test
public void propertyoverrideconfigurertest() {
ClassPathXmlApplicationContext ac
= new ClassPathXmlApplicationContext("classpath:propertyoverrideconfigurertest/propertyoverrideconfigurertest.xml");
System.out.println(ac.getBean("myPropertyOverrideConfigurerBean"));
}
运行:
{"age":0,"name":"李四"}
Process finished with exit code 0
可以看到name属性已经替换成李四
了。
4:源码分析
该类与PropertyPlaceholderConfiguer一样,同样继承自PropertyResourceConfigurer
,PropertyResourceConfigurer实现了postProcessBeanFactory
方法,完成了获取配置文件信息等工作,然后调用processProperties
方法执行具体的替换逻辑,我们就从这个方法开始分析。
4.1:processProperties
org.springframework.beans.factory.config.PropertyOverrideConfigurer#processProperties
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
// 循环属性的名称,如myPropertyOverrideConfigurerBean.name=李四
// myPropertyOverrideConfigurerBean.age=30这里获取的就是[myPropertyOverrideConfigurerBean.name,myPropertyOverrideConfigurerBean.age]
for (Enumeration<?> names = props.propertyNames(); names.hasMoreElements();) {
// 配置文件的key,如myPropertyOverrideConfigurerBean.name
String key = (String) names.nextElement();
try {
// <2021-04-09 15:26>
// 替换属性,props.getProperty(key)是获取要替换的新值
processKey(beanFactory, key, props.getProperty(key));
}
catch (BeansException ex) {
String msg = "Could not process key '" + key + "' in PropertyOverrideConfigurer";
if (!this.ignoreInvalidKeys) {
throw new BeanInitializationException(msg, ex);
}
if (logger.isDebugEnabled()) {
logger.debug(msg, ex);
}
}
}
}
<2021-04-09 15:26>
处就是执行具体的属性替换的方法,详细参考4.2:processKey
。
4.2:processKey
源码如下:
org.springframework.beans.factory.config.PropertyOverrideConfigurer#processKey
protected void processKey(ConfigurableListableBeanFactory factory, String key, String value)
throws BeansException {
// 获取分隔符.的位置
int separatorIndex = key.indexOf(this.beanNameSeparator);
// 如果是没有分隔符.则抛出异常,处理配置错误的情况
if (separatorIndex == -1) {
throw new BeanInitializationException("Invalid key '" + key +
"': expected 'beanName" + this.beanNameSeparator + "property'");
}
// 获取bean名称
String beanName = key.substring(0, separatorIndex);
// 获取要替换的属性名称
String beanProperty = key.substring(separatorIndex + 1);
this.beanNames.add(beanName);
// <2021-04-09 16:49>,执行替换
applyPropertyValue(factory, beanName, beanProperty, value);
if (logger.isDebugEnabled()) {
logger.debug("Property '" + key + "' set to value [" + value + "]");
}
}
<2021-04-09 16:49>
处源码如下:
org.springframework.beans.factory.config.PropertyOverrideConfigurer#applyPropertyValue
protected void applyPropertyValue(
ConfigurableListableBeanFactory factory, String beanName, String property, String value) {
BeanDefinition bd = factory.getBeanDefinition(beanName);
BeanDefinition bdToUse = bd;
while (bd != null) {
bdToUse = bd;
bd = bd.getOriginatingBeanDefinition();
}
// 使用新的替换值定义PropertyValue
PropertyValue pv = new PropertyValue(property, value);
pv.setOptional(this.ignoreInvalidKeys);
// 添加新的替换老的
bdToUse.getPropertyValues().addPropertyValue(pv);
}