原文地址: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" />

SPRING多个占位符配置文件解析源码研究--转的更多相关文章

  1. spring源码分析之配置文件名占位符的解析(一)

    一.直接写个测试例子 package com.test; import org.junit.Test; import org.springframework.context.ApplicationCo ...

  2. Spring Cloud Finchley.SR1 版本的坑:placeholer占位符无法解析!

    接入nacos 之后,想把所有的配置丢上去. 启动程序是: @EnableDiscoveryClient @RestController @ComponentScan(basePackages = { ...

  3. Spring 利用PropertyPlaceholderConfigurer占位符

      Hey Girl   博客园    首页    博问    闪存    新随笔    订阅     管理 posts - 42,  comments - 3,  trackbacks - 0 Sp ...

  4. dubbo注册中心占位符无法解析问题

    dubbo注册中心占位符无法解析问题 1.背景 最近搞了2个老项目,想把他们融合到一起.这俩项目情况简介如下: 项目一:基于SpringMVC + dubbo,配置读取本地properties文件,少 ...

  5. Spring中AOP相关的API及源码解析

    Spring中AOP相关的API及源码解析 本系列文章: 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring ...

  6. dubbo注册中心占位符无法解析问题(二)

    dubbo注册中心占位符无法解析问题 前面分析了dubbo注册中心占位符无法解析的问题. 并给出了2种解决办法: 降低mybatis-spring的版本至2.0.1及以下 自定义MapperScann ...

  7. spring事务详解(三)源码详解

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...

  8. Flink 源码解析 —— 源码编译运行

    更新一篇知识星球里面的源码分析文章,去年写的,周末自己录了个视频,大家看下效果好吗?如果好的话,后面补录发在知识星球里面的其他源码解析文章. 前言 之前自己本地 clone 了 Flink 的源码,编 ...

  9. Spring Boot核心技术之Rest映射以及源码的分析

    Spring Boot核心技术之Rest映射以及源码的分析 该博客主要是Rest映射以及源码的分析,主要是思路的学习.SpringBoot版本:2.4.9 环境的搭建 主要分两部分: Index.ht ...

随机推荐

  1. BCD码和十六进制,十进制转换

    参考文档: http://wenku.baidu.com/link?url=CfK2Wl7sCEmpzEabnbHSbcwf2t4yoSH6_n8sUIRw54piWaRB7hZ6RkaStWEkbC ...

  2. 输出日志实例改成用Spring的AOP来实现

    1.采用Interception Around通知的形式实现 Interception Around通知会在Join Point的前后执行,实现Interception Around通知的类需要实现接 ...

  3. 利用闭包实现多次ajax请求只执行最后一次

    点一个按钮,则向服务器请求资源,不作处理时,多次点击后会有很多个请求在等待.我们知道一般我们用ajax是异步请求,那么我们快速重复点击一个按钮得到的结果其实我们并不知道是哪次点击的结果可能是第一次可能 ...

  4. ES6的promise对象应该这样用

    ES6修补了一位Js修真者诸多的遗憾. 曾几何时,我这个小白从js非阻塞特性的坑中爬出来,当我经历了一些回调丑陋的写法和优化的尝试之后,我深深觉得js对于多线程阻塞式的开发语言而言,可能有着其太明显的 ...

  5. spring注解配置实例

    在spring中使用注解配置前需要先在配置文件指定需要扫描的包. 通过注解的方式依赖注入,可以不用创建set方法,也不用在xml文件中申明注入关系. 实例结构如下: 整个流程是: 先创建好数据库的表对 ...

  6. DSY3163*Eden的新背包问题

    Description "寄没有地址的信,这样的情绪有种距离,你放着谁的歌曲,是怎样的心心静,能不能说给我听."失忆的Eden总想努力地回忆起过去,然而总是只能清晰地记得那种思念的 ...

  7. 单例模式和angular的services的使用方法

    在现实生活中存在着有这样的特点的一些类: A.这些类只能有一个实例: B.这些能够自动实例化: C.这个类对整个系统可见,即必须向整个系统提供这个实例. 不妨举一个具体的单例模式的例子:比如教室里面的 ...

  8. WebService创建与使用

    因为项目中需要实现客户端与服务器端的数据交换,以及获取服务器端其他程序的分析结果,所以对WebService做了些简单的了解,现记录如下: 一.WebService程序编写 1.  在VS中新建空白网 ...

  9. Curator Framework的基本使用方法

    Curator Framework提供了简化使用zookeeper更高级的API接口.它包涵很多优秀的特性,主要包括以下三点: 自动连接管理:自动处理zookeeper的连接和重试存在一些潜在的问题: ...

  10. 如何创建一个AJAX-Enabled WCF Service

      原创地址:http://www.cnblogs.com/jfzhu/p/4041638.html 转载请注明出处   前面的文章中介绍过<Step by Step 创建一个WCF Servi ...