记一则对SpringBoot@PropertySource不能解析yml和yaml文件的问题

scio

https://github.com/rench/scio

起因

在项目中,有一个文件core.yml,里面存储了core模块的一些基础配置,该文件里面的写法有properties的写法也有yaml的写法。例如:

···
scio.core.api.secrets: test,123456
scio.core.sms.appKey=1d5123123165db5
···

在eclipse编辑器中,经常会提示错误,也就是说yml文件中的语法有误。确实,scio.core.sms.appKey=1d5123123165db5这段的写法是properties的写法。但是启动程序,没有任何错误报出,看着不爽,就想把错误取消,所以把scio.core.sms.appKey=1d5123123165db5修改成scio.core.sms.appKey: "1d5123123165db5"。启动项目,junit测试报错了,提示appKey不存在。

发现

发现junit测试错误,根据问题找到校验appKey的业务逻辑,发现${scio.core.sms.appKey}获取到的appKey居然是带有引号的"1d5123123165db5"。那就奇怪了,按照yaml的写法,双引号和单引号只是区分是否转义字符串中的特殊符号,不会把双引号到值里面去的。跟踪发现,core.yml的引用是使用了@PropertySource({"classpath:core.yml"})进行解析的,通过查看@PropertySource注解的解释,如下:Given a file app.properties containing the key/value pair testbean.name=myTestBean, the following @Configuration classuses @PropertySource to contribute app.properties to the Environment's set of PropertySources.,由此可见,默认他是解析的properties文件,猜想,他并没有按照yaml的格式解析,若properties的解析也支持冒号分隔,那么把冒号后面的数据解析带有引号,也就能解释通了。

解析

  • @PropertySource 的注解中,有一个factory属性,可指定一个自定义的PropertySourceFactory接口实现,用于解析指定的文件。默认的实现是DefaultPropertySourceFactory,继续跟进,使用了PropertiesLoaderUtils.loadProperties进行文件解析,所以默认就是使用Properties进行解析的。

扩展

  • CompositePropertySourceFactory 查看了DefaultPropertySourceFactory的解析方法后,发现其支持properties文件的解析,跟进properties的load方法发现在解析时,分隔符是=或者:,虽说可以解析简单的yml格式内容,但是无法支持真正的yaml语法,可以对DefaultPropertySourceFactory进行扩展,支持两种格式混合解析。以下是主要代码
package com.scio.cloud.cloudconfig;

import java.io.IOException;
import java.util.Optional;
import java.util.Properties;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
/**
 * CompositePropertySourceFactory support properties and yaml file
 *
 * @author Wang.ch
 * @date 2019-03-22 09:30:15
 */
public class CompositePropertySourceFactory extends DefaultPropertySourceFactory {
  @Override
  public PropertySource<?> createPropertySource(String name, EncodedResource resource)
      throws IOException {
    String sourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());
    if (!resource.getResource().exists()) {
      // return an empty Properties
      return new PropertiesPropertySource(sourceName, new Properties());
    } else if (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml")) {
      Properties propertiesFromYaml = loadYaml(resource);
      return new PropertiesPropertySource(sourceName, propertiesFromYaml);
    } else {
      return super.createPropertySource(name, resource);
    }
  }
  /**
   * load yaml file to properties
   *
   * @param resource
   * @return
   * @throws IOException
   */
  private Properties loadYaml(EncodedResource resource) throws IOException {
    YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
    factory.setResources(resource.getResource());
    factory.afterPropertiesSet();
    return factory.getObject();
  }
}
  • @PropertySource
@PropertySource(
    value = {"core.yml", "key.properties"},
    factory = CompositePropertySourceFactory.class)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容