Spring配置文件:java
<context:property-placeholder location="classpath:/settings.properties" /> <context:property-placeholder location="classpath:/conf.properties"/>
settings.propertiesredis
redis.masterName=mymaster
conf.propertiesspring
home.abroad=1
測試類:app
@Component public class PropertyPlaceHolderTest { @Value("${redis.masterName}") String masterName; @Value("${home.abroad}") int homeAbroad; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext-test.xml"); PropertyPlaceHolderTest bean = ctx.getBean(PropertyPlaceHolderTest.class); System.out.println(ToStringBuilder.reflectionToString(bean)); } }
執行上述測試類 報錯:ide
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'home.abroad' in string value [${home.abroad}] at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:173) at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:125) at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:151) at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:142) at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:169) at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:748) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:745) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:735) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478) ... 15 more
爲何只能解析出第一個property-placeholder配置中的key?第二個property-placeholder配置文件中的key是被忽略了嗎?post
但明明日誌顯示兩個配置文件均被成功解析了啊。測試
INFO org.springframework.context.support.PropertySourcesPlaceholderConfigurer Loading properties file from class path resource [settings.properties] INFO org.springframework.context.support.PropertySourcesPlaceholderConfigurer Loading properties file from class path resource [conf.properties]
那究竟是什麼緣由呢?只有分析源代碼了。發現一旦解析配置文件後會將其封裝到一個PropertySourcesPropertyResolver對象中,以下所示:ui
//所屬類:PropertySourcesPlaceholderConfigurer @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { //...... this.processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources)); }
再建立一個StringValueResolver對象,實際使用上面的PropertySourcesPropertyResolver對象來解析佔位符中的字符串。this
//所屬類:PropertySourcesPlaceholderConfigurer protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, final ConfigurablePropertyResolver propertyResolver) throws BeansException { //...... StringValueResolver valueResolver = new StringValueResolver() { public String resolveStringValue(String strVal) { String resolved = ignoreUnresolvablePlaceholders ? propertyResolver.resolvePlaceholders(strVal) : propertyResolver.resolveRequiredPlaceholders(strVal); return (resolved.equals(nullValue) ? null : resolved); } }; doProcessProperties(beanFactoryToProcess, valueResolver); }
最後將此StringValueResolver對象放入到一個List中,spa
//所屬類:AbstractBeanFactory public void addEmbeddedValueResolver(StringValueResolver valueResolver) { Assert.notNull(valueResolver, "StringValueResolver must not be null"); this.embeddedValueResolvers.add(valueResolver); }
當自動注入@Value標註的字段時,
DEBUG org.springframework.beans.factory.annotation.InjectionMetadata Processing injected method of bean 'propertyPlaceHolderTest': AutowiredFieldElement for int com.tcl.test.PropertyPlaceHolderTest.homeAbroad
須要解析@Value中的佔位符中的內容了,涉及到源碼是:
//所屬類:AbstractBeanFactory public String resolveEmbeddedValue(String value) { String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { result = resolver.resolveStringValue(result); } return result; }
從以前描述可知this.embeddedValueResolvers中有兩個valueResolver對象(分別對應兩個context:property-placeholder元素),因而先取出第一個valueResolver(對應settings.properties)來解析${home.abroad},而它是配置在conf.poperties中的,確定解析不到,因而就報錯了,見源代碼:
//所屬的類與方法:PropertyPlaceholderHelper.parseStringValue if (propVal != null) { //...... } else if (this.ignoreUnresolvablePlaceholders) { // Proceed with unprocessed value. startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length()); } else { throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'" + " in string value [" + strVal + "]"); }
但明明才遍歷了List中第一個valueResolver對象呢,後面的那個能夠成功解析啊。錯誤拋的也太急了點吧。
Spring確定也考慮了這一點,從上面的源代碼也能夠看出,只要設置了ignoreUnresolvablePlaceholders爲true的話,就不會走最後的else分支了。
修改Spring配置,顯式指定第一個context:property-placeholder元素ignore-unresolvable屬性爲true,以下所示:
<context:property-placeholder location="classpath:/settings.properties" ignore-unresolvable="true"/> <context:property-placeholder location="classpath:/conf.properties"/>
這時就不會報錯了,${home.abroad}也能被成功解析了。見輸出:
com.tcl.test.PropertyPlaceHolderTest@2f949a6b[masterName=mymaster,homeAbroad=1]
固然正常狀況下不必配置成兩個或多個context:property-placeholder,徹底能夠用一個context:property-placeholder來指定多個配置文件,以下所示:
<context:property-placeholder location="classpath:/settings.properties,/conf.properties" />