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 方法抓取出来:

private void load0 (LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
int limit;
// 当前key所在位置
int keyLen;
// 当前value所在位置
int valueStart;
char c;//读取的字符
boolean hasSep;
boolean precedingBackslash;//是否转义字符,eg:/n etc.
// 一行一行地读取
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false; //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
precedingBackslash = false;
//key的长度小于总的字符长度,那么就进入循环
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
//need check if escaped.
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
//value的起始位置小于总的字符长度,那么就进入该循环
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
//当前字符是否非空格类字符
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
//读取key
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
//读取value
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
put(key, value);
}
}

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

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

# 测试ConfigurationProperties
user:
userName: root
isAdmin: true
regTime: 2019/11/01
isOnline: 1
maps: {k1 : v1,k2: v2}
lists:
- list1
- list2
address:
tel: 15899988899
name: 上海市

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

package com.example.springboot.properties.core.propertyResouceFactory;

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable; import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Properties; /**
* <pre>
* YAML配置文件读取工厂类
* </pre>
* <p>
* <pre>
* @author nicky.ma
* 修改记录
* 修改后版本: 修改人: 修改日期: 2019/11/13 15:44 修改内容:
* </pre>
*/
public class YamlPropertyResourceFactory implements PropertySourceFactory { /**
* Create a {@link PropertySource} that wraps the given resource.
*
* @param name the name of the property source
* @param encodedResource the resource (potentially encoded) to wrap
* @return the new {@link PropertySource} (never {@code null})
* @throws IOException if resource resolution failed
*/
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource encodedResource) throws IOException {
String resourceName = Optional.ofNullable(name).orElse(encodedResource.getResource().getFilename());
if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {//yaml资源文件
List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, encodedResource.getResource());
return yamlSources.get(0);
} else {//返回空的Properties
return new PropertiesPropertySource(resourceName, new Properties());
}
}
}

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

package com.example.springboot.properties.bean;

import com.example.springboot.properties.core.propertyResouceFactory.CommPropertyResourceFactory;
import com.example.springboot.properties.core.propertyResouceFactory.YamlPropertyResourceFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.stereotype.Component; import java.util.Date;
import java.util.List;
import java.util.Map; /**
* <pre>
*
* </pre>
*
* @author nicky
* <pre>
* 修改记录
* 修改后版本: 修改人: 修改日期: 2019年11月03日 修改内容:
* </pre>
*/
@Component
@PropertySource(value = "classpath:user.yml",encoding = "utf-8",factory = YamlPropertyResourceFactory.class)
@ConfigurationProperties(prefix = "user")
public class User { private String userName;
private boolean isAdmin;
private Date regTime;
private Long isOnline;
private Map<String,Object> maps;
private List<Object> lists;
private Address address; @Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", isAdmin=" + isAdmin +
", regTime=" + regTime +
", isOnline=" + isOnline +
", maps=" + maps +
", lists=" + lists +
", address=" + address +
'}';
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public boolean isAdmin() {
return isAdmin;
} public void setAdmin(boolean admin) {
isAdmin = admin;
} public Date getRegTime() {
return regTime;
} public void setRegTime(Date regTime) {
this.regTime = regTime;
} public Long getIsOnline() {
return isOnline;
} public void setIsOnline(Long isOnline) {
this.isOnline = isOnline;
} public Map<String, Object> getMaps() {
return maps;
} public void setMaps(Map<String, Object> maps) {
this.maps = maps;
} public List<Object> getLists() {
return lists;
} public void setLists(List<Object> lists) {
this.lists = lists;
} public Address getAddress() {
return address;
} public void setAddress(Address address) {
this.address = address;
}
}
package com.example.springboot.properties.bean;

/**
* <pre>
*
* </pre>
*
* @author nicky
* <pre>
* 修改记录
* 修改后版本: 修改人: 修改日期: 2019年11月03日 修改内容:
* </pre>
*/
public class Address {
private String tel;
private String name; @Override
public String toString() {
return "Address{" +
"tel='" + tel + '\'' +
", name='" + name + '\'' +
'}';
} public String getTel() {
return tel;
} public void setTel(String tel) {
this.tel = tel;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

junit测试类代码:

package com.example.springboot.properties;

import com.example.springboot.properties.bean.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
class SpringbootPropertiesConfigApplicationTests { @Autowired
User user; @Test
public void testConfigurationProperties(){
System.out.println(user);
} }
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类进行改写一下:

package com.example.springboot.properties.core.propertyResouceFactory;

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable; import java.io.IOException;
import java.util.List;
import java.util.Optional; /**
* <pre>
* 通用的资源文件读取工厂类
* </pre>
* <p>
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2019/11/25 10:35 修改内容:
* </pre>
*/
public class CommPropertyResourceFactory implements PropertySourceFactory { /**
* Create a {@link PropertySource} that wraps the given resource.
*
* @param name the name of the property source
* @param resource the resource (potentially encoded) to wrap
* @return the new {@link PropertySource} (never {@code null})
* @throws IOException if resource resolution failed
*/
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
String resourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());
if (resourceName.endsWith(".yml") || resourceName.endsWith(".yaml")) {
List<PropertySource<?>> yamlSources = new YamlPropertySourceLoader().load(resourceName, resource.getResource());
return yamlSources.get(0);
} else {
return new DefaultPropertySourceFactory().createPropertySource(name, resource);
}
}
}

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

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

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

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下载链接

最新文章

  1. Delphi 中的自动释放策略-转
  2. Codeforces Round #380(div 2)
  3. phpMyAdmin登录出错
  4. nyoj163_Phone List_字典树
  5. HTTPS 概述
  6. 《JAVA与模式》之组合模式
  7. MySQL 强制操作以及order by 使用
  8. 在Android中将子View的坐标转换为父View的坐标
  9. jQuery 2.0.3 源码分析 Deferrred概念
  10. Mac下的SVN客户端工具Cornerstone使用教程
  11. 最新app源码下载:200款优秀Android项目源码
  12. 九度 1420 Jobdu MM分水果 -- 动态规划、深度优先搜索
  13. 使用Xcode插件,让iOS开发更加便捷
  14. Validation of viewstate MAC failed machinekey生成、使用方法
  15. Windows Nodejs 安装教程
  16. Android Studio安装Genymotion插件
  17. use snippet save dom to excel
  18. 29.Mysql监控
  19. Console控制台的正确打开方式
  20. No Spring WebApplicationInitializer types detected on classpath异常的解决

热门文章

  1. python将数据库修改,数据库操作同步到数据库中
  2. ChinaSys 一些心得
  3. Java 从入门到进阶之路(十四)
  4. 爬虫(十):AJAX、爬取AJAX数据
  5. cesium 实现风场图效果(附源码下载)
  6. 65-如何部署 Calico 网络?
  7. 最短路径之Dijsktra算法(python)
  8. 【HNOI 2019】JOJO
  9. linux下的服务器上传与下载
  10. druid链接数据库