PropertyPlaceholderConfigurer加载报错Could not resolve placeholder

加载文件问题

1. 使用@PropertySource(“classpath:application.properties”)和@Value加载配置文件属性的时候启动报错
@Configuration
@PropertySource("classpath:application.properties")
@Data
@Component
public class CacheConfigBean {
    private int retryCount;
    private int elapsedTimeMs;
    private String connectString;
    private int sessionTimeoutMs ;
    private int connectionTimeoutMs;
    @Value("${spring.cache.caffeine.spec}")
    private String springCacheCaffeineSpec;
    @Value("${zookeeper.redis.node}")
    private String zookeeperRedisNode;
    @Value("${DAFULT_REDIS_TIME_OUT}")
    private String dafultRedisTimeOut;
}
2. 启动的报错日志
Caused by: java.lang.IllegalArgumentException: 
Could not resolve placeholder 'spring.cache.caffeine.spec' in value "${spring.cache.caffeine.spec}"
是哪里配置出问题了呢,因为所有的module的配置项都是一样的,其他的都可以读取配置文件信息,并且替换属性。为啥到这里就直接报错,启动报错直接终止了。
3. 分析原因

寻找module的不同点,在一个不起眼的角落的配置引起了我的注意,一个名为:spring-all.xml的配置文件里面配置了PropertyPlaceholderConfigurer。这个配置目的是为了把xml中的"$ {}“可以用其他的properties中的属性在beanfactorypostProcoessor容器后置处理器中进行替换BeanDefinition里面的”${}"属性。

<!-- 配置的资源文件 -->
<bean id="propertyConfigurer"
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"></property>
    <property name="locations">
        <list>
            <value>classpath:dubbo-customer.properties</value>
            <value>classpath:dubbo-provider.properties</value>
        </list>
    </property>
</bean>
PropertyPlaceholderConfigurer是把所有locations配置的所有配置文件的属性全部加载到Properties缓存区里面
  1. 加载配置文件和属性postProcessBeanFactory是通过Properties mergedProps = mergeProperties();加载合并的配置文件的属性
@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		try {
		    // 加载所有的locations配置文件的属性再合并到一个Properties里面
			Properties mergedProps = mergeProperties();

			// Convert the merged properties, if necessary.
			convertProperties(mergedProps);

			// Let the subclass process the properties.
			processProperties(beanFactory, mergedProps);
		}
		catch (IOException ex) {
			throw new BeanInitializationException("Could not load properties", ex);
		}
	}
  1. 把location的配置文件属性加载并添加到props里面loadProperties
protected void loadProperties(Properties props) throws IOException {
		if (this.locations != null) {
			for (Resource location : this.locations) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading properties file from " + location);
				}
				try {
				    // 把location的配置文件属性加载并添加到props里面
					PropertiesLoaderUtils.fillProperties(
							props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
				}
				catch (FileNotFoundException | UnknownHostException ex) {
					if (this.ignoreResourceNotFound) {
						if (logger.isDebugEnabled()) {
							logger.debug("Properties resource not found: " + ex.getMessage());
						}
					}
					else {
						throw ex;
					}
				}
			}
		}
	}
  1. 再封装StringValueResolver解析器,然后把这个解析器存入PlaceholderConfigurerSupport的embeddedValueResolvers里面,以待后续@Value解析使用替换指定的字符串
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			StringValueResolver valueResolver) {

		BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

		String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
		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);
	}
  1. addEmbeddedValueResolver把解析器存入PlaceholderConfigurerSupport的embeddedValueResolvers里面。这里添加进入进入的顺序是系统默认读取的顺序,是不可控的
@Override
	public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
		Assert.notNull(valueResolver, "StringValueResolver must not be null");
		this.embeddedValueResolvers.add(valueResolver);
	}
  1. 加载资源的BeanFactoryPostProcessors去解析配置文件的PropertyPlaceholderConfigurer把解析好的文件属性存入properties缓存里面,但是注解@PropertySource是使用的PropertySourcesPlaceholderConfigurer去load加载配置文件的属性,然后存入它自己的localproperties缓存。
    相关连PropertyPlaceholderConfigurer和PropertySourcesPlaceholderConfigurer信息
  2. 因为这里添加的顺序的不可控,导致出现了Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder ‘spring.cache.caffeine.spec’ in value “${spring.cache.caffeine.spec}”;首先加载第一个是PropertyPlaceholderConfigurer,然后才是PropertySourcesPlaceholderConfigurer;
  • PropertyPlaceholderConfigure是没有spring.cache.caffeine.spec属性
  • PropertySourcesPlaceholderConfigurer有spring.cache.caffeine.spec属性
  1. 经过类初始化过程:getBean->doGetBean->createBean
  • createBeanInstance:通过策略创建bean包装实例——这里可能回去递归初始化构造器中参数的类
  • 调用所有的 MergedBeanDefinitionPostProcessors,一个扩展点
  • 提前暴露引用,允许循环引用,一般为 true
  • populateBean:注入依赖类和相关属性——这里递归初始化注解的依赖类
  • initializeBean:调用初始化方法
  • registerDisposableBeanIfNecessary:搬到 bean 销毁的方法
  1. resolveEmbeddedValue解析@Value中的"${}"相关的字段属性,并且从properties中的属性做替换。
@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
	if (value == null) {
		return null;
	}
	String result = value;
	// 这里的顺序:0- PropertyPlaceholderConfigure缓存解析器
	//             1- PropertySourcesPlaceholderConfigure缓存解析器
	for (StringValueResolver resolver : this.embeddedValueResolvers) {
		result = resolver.resolveStringValue(result);
		if (result == null) {
			return null;
		}
	}
	return result;
}
  1. 由于使用第一个PropertyPlaceholderConfigure缓存解析器解析对应的@Value("${spring.cache.caffeine.spec}") 字符串失败,直接报错 Could not resolve placeholder ‘spring.cache.caffeine.spec’ in value "
    ${spring.cache.caffeine.spec}" 。这里直接报错以后是不会再走后面的流程,所以是不会去调用PropertySourcesPlaceholderConfigure缓存解析器再解析${spring.cache.caffeine.spec} 这个配置属性。程序启动直接终止,整个启动直接玩完。
脑瓜子嗡嗡的了吧,是不是这个是检查其他的配置都是一样的,但是这个项目就是加载配置属性的时候报错,然后你就会和我一样马上徘徊在类加载的配置文件加载解析和BEAN初始化后对属性填充替换进行源码分析里遨游了。

给你提供解决方案
  1. 某些业务可以直接把配置项去掉,使用默认的配置加载,就不会再出现此类不好寻找的问题了。
  2. 也可以直接把PropertyPlaceholderConfigure修改为PropertySourcesPlaceholderConfigurer就可以了。
    <!-- 配置的资源文件 -->
    <bean id="propertyConfigurer"
          class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="ignoreResourceNotFound" value="true"></property>
        <property name="locations">
            <list>
                <value>classpath:dubbo-customer.properties</value>
                <value>classpath:dubbo-provider.properties</value>
            </list>
        </property>
    </bean>
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值