SPRING多個占位符配置文件解析源碼研究--轉


原文地址:http://www.cnphp6.com/archives/85639

Spring配置文件:

<context:property-placeholder location="classpath:/settings.properties" />
<context:property-placeholder location="classpath:/conf.properties"/>

settings.properties

redis.masterName=mymaster

conf.properties

home.abroad=1

測試類:

@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));
    }
}

執行上述測試類 報錯:

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是被忽略了嗎?

但明明日志顯示兩個配置文件均被成功解析了啊。

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對象中,如下所示:

//所屬類:PropertySourcesPlaceholderConfigurer
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        
    //......   
    this.processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
}

再創建一個StringValueResolver對象,實際使用上面的PropertySourcesPropertyResolver對象來解析占位符中的字符串。

//所屬類: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中,

//所屬類: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" />


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM