上一篇我们说到父子容器无法共享properties属性值的问题,这次问题发生在同一容器中,废话不多说,上代码
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test01.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test02.properties</value>
</list>
</property>
</bean>
<!-- test01.properties 和 test02.properties 的属性值互不相同 -->
<bean class="cn.fg.bean.Person">
<property name="name" value="${name1}" />
</bean>
启程程序后,控制台报错Could not resolve placeholder 'name1' in value "${name1}"
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'cn.fg.bean.Person#0' defined in class path resource [spring-mvc.xml]: Could not resolve placeholder 'name1' in value "${name1}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'name1' in value "${name1}"
at org.springframework.beans.factory.config.PlaceholderConfigurerSupport.doProcessProperties(PlaceholderConfigurerSupport.java:223)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.processProperties(PropertySourcesPlaceholderConfigurer.java:180)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:152)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:283)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:163)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524)
at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668)
为什么?我们点进PlaceholderConfigurerSupport.java:223行、PropertySourcesPlaceholderConfigurer.java:180行
/**
* 先看PropertySourcesPlaceholderConfigurer.java:180行,这个函数是在替换占位符
*
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
propertyResolver.setValueSeparator(this.valueSeparator);
StringValueResolver valueResolver = new StringValueResolver() {
@Override
public String resolveStringValue(String strVal) {
String resolved = (ignoreUnresolvablePlaceholders ?
propertyResolver.resolvePlaceholders(strVal) :
propertyResolver.resolveRequiredPlaceholders(strVal));
if (trimValues) {
resolved = resolved.trim();
}
return (resolved.equals(nullValue) ? null : resolved);
}
};
//这里点进去就是PlaceholderConfigurerSupport.java:223行
doProcessProperties(beanFactoryToProcess, valueResolver);
}
//PlaceholderConfigurerSupport.java:223行
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
//这里正在遍历所有定义的bean,最终会进入catch报错
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);
}
为什么会报错?PropertyPlaceholderConfigurer在初始化时会调用上面两个方法,遍历其他所有的bean定义信息,看有没有使用占位符,然后与自身中的properties对比,是否存在该占位符的属性,若不存在就会报错;由于我们这里定义了两个PropertyPlaceholderConfigurer,而properties的属性也不一样;当其中一个遍历所有bean时,肯定会找不属性,所以就报错了。如何解决?
方式一:两个properties写到一个PropertyPlaceholderConfigurer中
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test01.properties</value>
<value>classpath:test02.properties</value>
</list>
</property>
</bean>
方式二:设置忽略未能解析的占位符为true
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test01.properties</value>
</list>
</property>
//增加该属性值为true
<property name="ignoreUnresolvablePlaceholders" value="true"></property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:test02.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"></property>
</bean>