SpringBoot系列之@PropertySource支持yaml文件读取

最近在做实验,想通过@PropertySource注解读取配置文件的属性,进行映射,习惯上用properties都是测试没问题的,偶然换成yaml文件,发现都读取不到属性值

因为yaml语法很简洁,比较喜欢写yaml配置文件,很显然,@PropertySource默认不支持yaml读取,我们改成@Value注解也是可以读取的,不过属性一堆的话,一个一个读取也是很繁琐的,通过网上找资料和自己实验验证,发现是可以实现对yaml支持

然后,为什么@PropertySource注解默认不支持?可以简单跟一下源码

@PropertySource源码:



根据注释,默认使用DefaultPropertySourceFactory类作为资源文件加载类



里面还是调用Spring框架底层的PropertiesLoaderUtils工具类进行读取的



PropertiesLoaderUtils.loadProperties



从源码可以看出也是支持xml文件读取的,能支持reader就获取reader对象,否则出件inputStream



load0方法是关键,这里加了同步锁



很重要的load0 方法抓取出来:

  1. private void load0 (LineReader lr) throws IOException {
  2. char[] convtBuf = new char[1024];
  3. int limit;
  4. // 当前key所在位置
  5. int keyLen;
  6. // 当前value所在位置
  7. int valueStart;
  8. char c;//读取的字符
  9. boolean hasSep;
  10. boolean precedingBackslash;//是否转义字符,eg:/n etc.
  11. // 一行一行地读取
  12. while ((limit = lr.readLine()) >= 0) {
  13. c = 0;
  14. keyLen = 0;
  15. valueStart = limit;
  16. hasSep = false;
  17. //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
  18. precedingBackslash = false;
  19. //key的长度小于总的字符长度,那么就进入循环
  20. while (keyLen < limit) {
  21. c = lr.lineBuf[keyLen];
  22. //need check if escaped.
  23. if ((c == '=' || c == ':') && !precedingBackslash) {
  24. valueStart = keyLen + 1;
  25. hasSep = true;
  26. break;
  27. } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
  28. valueStart = keyLen + 1;
  29. break;
  30. }
  31. if (c == '\\') {
  32. precedingBackslash = !precedingBackslash;
  33. } else {
  34. precedingBackslash = false;
  35. }
  36. keyLen++;
  37. }
  38. //value的起始位置小于总的字符长度,那么就进入该循环
  39. while (valueStart < limit) {
  40. c = lr.lineBuf[valueStart];
  41. //当前字符是否非空格类字符
  42. if (c != ' ' && c != '\t' && c != '\f') {
  43. if (!hasSep && (c == '=' || c == ':')) {
  44. hasSep = true;
  45. } else {
  46. break;
  47. }
  48. }
  49. valueStart++;
  50. }
  51. //读取key
  52. String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
  53. //读取value
  54. String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
  55. put(key, value);
  56. }
  57. }

ok,从源码可以看出,这个方法是一行一行地读取,然后根据冒号、等于号、空格等进行校验,经过一系列遍历之后获取key和value,而yaml语法是以缩进来辨别的,经过自己调试,这个方法也是不支持yaml文件的读取的,properties源码是比较多的,具体的Properties源码实现的可以参考博客:https://www.cnblogs.com/liuming1992/p/4360310.html,这篇博客写的比较详细

ok,然后给个例子来实现对yaml配置文件的读取

  1. # 测试ConfigurationProperties
  2. user:
  3. userName: root
  4. isAdmin: true
  5. regTime: 2019/11/01
  6. isOnline: 1
  7. maps: {k1 : v1,k2: v2}
  8. lists:
  9. - list1
  10. - list2
  11. address:
  12. tel: 15899988899
  13. name: 上海市

模仿DefaultPropertySourceFactory写一个yaml资源文件读取的工厂类:

  1. package com.example.springboot.properties.core.propertyResouceFactory;
  2. import org.springframework.boot.env.YamlPropertySourceLoader;
  3. import org.springframework.core.env.PropertiesPropertySource;
  4. import org.springframework.core.env.PropertySource;
  5. import org.springframework.core.io.support.EncodedResource;
  6. import org.springframework.core.io.support.PropertySourceFactory;
  7. import org.springframework.lang.Nullable;
  8. import java.io.IOException;
  9. import java.util.List;
  10. import java.util.Optional;
  11. import java.util.Properties;
  12. /**
  13. * <pre>
  14. * YAML配置文件读取工厂类
  15. * </pre>
  16. * <p>
  17. * <pre>
  18. * @author nicky.ma
  19. * 修改记录
  20. * 修改后版本: 修改人: 修改日期: 2019/11/13 15:44 修改内容:
  21. * </pre>
  22. */
  23. public class YamlPropertyResourceFactory implements PropertySourceFactory {
  24. /**
  25. * Create a {@link PropertySource} that wraps the given resource.
  26. *
  27. * @param name the name of the property source
  28. * @param encodedResource the resource (potentially encoded) to wrap
  29. * @return the new {@link PropertySource} (never {@code null})
  30. * @throws IOException if resource resolution failed
  31. */
  32. @Override
  33. public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource encodedResource) throws IOException {
  34. String resourceName = Optional.ofNullable(name).orElse(encodedResource.getResource().getFilename());
  35. if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {//yaml资源文件
  36. List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, encodedResource.getResource());
  37. return yamlSources.get(0);
  38. } else {//返回空的Properties
  39. return new PropertiesPropertySource(resourceName, new Properties());
  40. }
  41. }
  42. }

写个bean类进行属性映射,注意换一下factory参数,factory = YamlPropertyResourceFactory.class

  1. package com.example.springboot.properties.bean;
  2. import com.example.springboot.properties.core.propertyResouceFactory.CommPropertyResourceFactory;
  3. import com.example.springboot.properties.core.propertyResouceFactory.YamlPropertyResourceFactory;
  4. import org.springframework.boot.context.properties.ConfigurationProperties;
  5. import org.springframework.context.annotation.PropertySource;
  6. import org.springframework.core.io.support.DefaultPropertySourceFactory;
  7. import org.springframework.stereotype.Component;
  8. import java.util.Date;
  9. import java.util.List;
  10. import java.util.Map;
  11. /**
  12. * <pre>
  13. *
  14. * </pre>
  15. *
  16. * @author nicky
  17. * <pre>
  18. * 修改记录
  19. * 修改后版本: 修改人: 修改日期: 2019年11月03日 修改内容:
  20. * </pre>
  21. */
  22. @Component
  23. @PropertySource(value = "classpath:user.yml",encoding = "utf-8",factory = YamlPropertyResourceFactory.class)
  24. @ConfigurationProperties(prefix = "user")
  25. public class User {
  26. private String userName;
  27. private boolean isAdmin;
  28. private Date regTime;
  29. private Long isOnline;
  30. private Map<String,Object> maps;
  31. private List<Object> lists;
  32. private Address address;
  33. @Override
  34. public String toString() {
  35. return "User{" +
  36. "userName='" + userName + '\'' +
  37. ", isAdmin=" + isAdmin +
  38. ", regTime=" + regTime +
  39. ", isOnline=" + isOnline +
  40. ", maps=" + maps +
  41. ", lists=" + lists +
  42. ", address=" + address +
  43. '}';
  44. }
  45. public String getUserName() {
  46. return userName;
  47. }
  48. public void setUserName(String userName) {
  49. this.userName = userName;
  50. }
  51. public boolean isAdmin() {
  52. return isAdmin;
  53. }
  54. public void setAdmin(boolean admin) {
  55. isAdmin = admin;
  56. }
  57. public Date getRegTime() {
  58. return regTime;
  59. }
  60. public void setRegTime(Date regTime) {
  61. this.regTime = regTime;
  62. }
  63. public Long getIsOnline() {
  64. return isOnline;
  65. }
  66. public void setIsOnline(Long isOnline) {
  67. this.isOnline = isOnline;
  68. }
  69. public Map<String, Object> getMaps() {
  70. return maps;
  71. }
  72. public void setMaps(Map<String, Object> maps) {
  73. this.maps = maps;
  74. }
  75. public List<Object> getLists() {
  76. return lists;
  77. }
  78. public void setLists(List<Object> lists) {
  79. this.lists = lists;
  80. }
  81. public Address getAddress() {
  82. return address;
  83. }
  84. public void setAddress(Address address) {
  85. this.address = address;
  86. }
  87. }
  1. package com.example.springboot.properties.bean;
  2. /**
  3. * <pre>
  4. *
  5. * </pre>
  6. *
  7. * @author nicky
  8. * <pre>
  9. * 修改记录
  10. * 修改后版本: 修改人: 修改日期: 2019年11月03日 修改内容:
  11. * </pre>
  12. */
  13. public class Address {
  14. private String tel;
  15. private String name;
  16. @Override
  17. public String toString() {
  18. return "Address{" +
  19. "tel='" + tel + '\'' +
  20. ", name='" + name + '\'' +
  21. '}';
  22. }
  23. public String getTel() {
  24. return tel;
  25. }
  26. public void setTel(String tel) {
  27. this.tel = tel;
  28. }
  29. public String getName() {
  30. return name;
  31. }
  32. public void setName(String name) {
  33. this.name = name;
  34. }
  35. }

junit测试类代码:

  1. package com.example.springboot.properties;
  2. import com.example.springboot.properties.bean.User;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. @SpringBootTest
  7. class SpringbootPropertiesConfigApplicationTests {
  8. @Autowired
  9. User user;
  10. @Test
  11. public void testConfigurationProperties(){
  12. System.out.println(user);
  13. }
  14. }
  1. User{userName='root(15899988899)', isAdmin=false, regTime=Fri Nov 01 00:00:00 SGT 2019, isOnline=1, maps={k2=v2, k1=-30363940}, lists=[1f90e323-8a9c-4194-a31c-be9abbe9ce38, a869f68947faa92964d2a36ce86ee980], address=Address{tel='15899988899', name='上海浦东区'}}

如果既要支持原来的yaml,又要支持properties,就可以将propertyResourceFactory类进行改写一下:

  1. package com.example.springboot.properties.core.propertyResouceFactory;
  2. import org.springframework.boot.env.YamlPropertySourceLoader;
  3. import org.springframework.core.env.PropertySource;
  4. import org.springframework.core.io.support.DefaultPropertySourceFactory;
  5. import org.springframework.core.io.support.EncodedResource;
  6. import org.springframework.core.io.support.PropertySourceFactory;
  7. import org.springframework.lang.Nullable;
  8. import java.io.IOException;
  9. import java.util.List;
  10. import java.util.Optional;
  11. /**
  12. * <pre>
  13. * 通用的资源文件读取工厂类
  14. * </pre>
  15. * <p>
  16. * <pre>
  17. * @author mazq
  18. * 修改记录
  19. * 修改后版本: 修改人: 修改日期: 2019/11/25 10:35 修改内容:
  20. * </pre>
  21. */
  22. public class CommPropertyResourceFactory implements PropertySourceFactory {
  23. /**
  24. * Create a {@link PropertySource} that wraps the given resource.
  25. *
  26. * @param name the name of the property source
  27. * @param resource the resource (potentially encoded) to wrap
  28. * @return the new {@link PropertySource} (never {@code null})
  29. * @throws IOException if resource resolution failed
  30. */
  31. @Override
  32. public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
  33. String resourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());
  34. if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {
  35. List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, resource.getResource());
  36. return yamlSources.get(0);
  37. } else {
  38. return new DefaultPropertySourceFactory().createPropertySource(name, resource);
  39. }
  40. }
  41. }

调用的时候,要改一下factory参数

  1. @PropertySource(value = "classpath:user.yml",encoding = "utf-8",factory = CommPropertyResourceFactory.class)

这个类就可以支持原来的properties文件,也可以支持yaml文件

  1. User{userName='root(15899988899)', isAdmin=false, regTime=Fri Nov 01 00:00:00 SGT 2019, isOnline=1, maps={k2=v2, k1=-30363940}, lists=[1f90e323-8a9c-4194-a31c-be9abbe9ce38, a869f68947faa92964d2a36ce86ee980], address=Address{tel='15899988899', name='上海浦东区'}}

代码下载:github下载链接

SpringBoot系列之@PropertySource读取yaml文件的更多相关文章

  1. SpringBoot系列之@PropertySource用法简介

    SpringBoot系列之@PropertySource用法简介 继上篇博客:SpringBoot系列之@Value和@ConfigurationProperties用法对比之后,本博客继续介绍一下@ ...

  2. Python读取Yaml文件

    近期看到好多使用Yaml文件做为配置文件或者数据文件的工程,随即也研究了下,发现Yaml有几个优点:可读性好.和脚本语言的交互性好(确实非常好).使用实现语言的数据类型.有一个一致的数据模型.易于实现 ...

  3. 使用python读取yaml文件

    在做APP测试时,通常需要把参数存到一个字典变量中,这时可以将参数写入yaml文件中,再读取出来. 新建yaml文件(android_caps.yaml),文件内容为: platformName: A ...

  4. java 读取 yaml 文件

        做 java 项目用的最多的配置文件就是 properites 或者 xml, xml 确实是被用烂了,Struts, Spring, Hibernate(ssh) 无一不用到 xml.相比厚 ...

  5. python读取yaml文件,在unittest中使用

    python读取yaml文件使用,有两种方式: 1.使用ddt读取 2,使用方法读取ddt的内容,在使用方法中进行调用 1.使用ddt读取 @ddt.ddt class loginTestPage(u ...

  6. Golang 入门系列(九) 如何读取YAML,JSON,INI等配置文件

    实际项目中,读取相关的系统配置文件是很常见的事情.今天就来说一说,Golang 是如何读取YAML,JSON,INI等配置文件的. 1. json使用 JSON 应该比较熟悉,它是一种轻量级的数据交换 ...

  7. day11_单元测试_读取yaml文件中的用例,自动获取多个yaml文件内容执行生成报告

    一.使用.yaml格式的文件直接可以存放字典类型数据,如下图,其中如果有-下一行有缩进代表这是个list,截图中是整体是一个list,其中有两部分,第二部分又包含另外一个list 二.单元测试:开发自 ...

  8. springBoot使用@Value标签读取*.properties文件的中文乱码问题

    上次我碰到获取properties文件中的中文出现乱码问题. 查了下资料,原来properties默认的字符编码格式为asci码,所以我们要对字符编码进行转换成UTF-8格式 原先代码:@Proper ...

  9. Eclipse 创建和读取yaml文件

    工具和用法: 1. eclipse插件包:org.dadacoalition.yedit_1.0.20.201509041456-RELEASE.jar 用法:将此jar包复制到eclipse-jee ...

随机推荐

  1. Lua-Async 协程的高级用法

    Lua-Async 这是一个基于协程的异步调用库, 该库的设计思路类似JavaScript的Promise, 但相比Promise, 它有更多的灵活性. -- 引入Async local Async ...

  2. 大数据学习笔记——Sqoop完整部署流程

    Sqoop详细部署教程 Sqoop是一个将hadoop与关系型数据库之间进行数据传输,批量数据导入导出的工具,注意,导入是指将数据从RDBMS导入到hadoop而导出则是指将数据从hadoop导出到R ...

  3. 【Zuul】使用学习

    [Zuul]使用学习 添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <arti ...

  4. C语言每日一练——第4题

    一.题目要求 已知数据文件in.dat中有300个四位数,并调用readDat()函数把这些数存储数组a中,编写函数jsValue(),其功能是:求出所有这些四位数是素数的个数cnt,再把所有满足此条 ...

  5. round分析

    Python 所谓的奇进偶弃,因为浮点数的表示在计算机中并不准确,用的时候可能要注意一下. 测试如下 print() 由运行得出结论: 当小数点左边为偶数:小数点右边X<6,舍 当小数点左边为偶 ...

  6. Python解密网易云音乐缓存文件获取MP3

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理.作者:GeneralMonkey Python解密网易云音乐缓存文件获取MP3 ...

  7. NetCore MemoryCache使用

    引用类库 1.Install-Package Microsoft.Extensions.Caching.Memory MemoryCacheOptions 缓存配置 1.ExpirationScanF ...

  8. C#程序编写高质量代码改善的157个建议【16-19】[动态数组、循环遍历、对象集合初始化]

    前言   软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开 ...

  9. 百度大脑EdgeBoard计算卡基于Resnet50/Mobile-SSD模型的性能评测

    ResNet模型 前言在上一次的测试中,我们从头开始训练了一个三个卷积层串联一个全连接层的输出,作为猫狗分类的预测的模型,这次我们自己训练一个ResNet模型,并在以下三个环境中进行性能的对比 AIS ...

  10. Java面试题_第四阶段

    1.1 电商行业特点 1.分布式 垂直拆分:根据功能模块进行拆分 水平拆分:根据业务层级进行拆分 2.高并发 用户单位时间内访问服务器数量,是电商行业中面临的主要问题 3.集群 抗击高兵发的有效手段, ...