文章目录
- 一.前言
- 1.语法
- 2.数据结构
- 2.1.对象
- 2.2.数组
- 2.3.普通属性
- 2.3.1.字符串
- 2.3.1.布尔值
- 2.3.2.整数
- 2.3.3.浮点数
- 2.3.4.Null
- 2.3.5.日期、时间
- 2.4.强制转换数据类型
- 2.5.引用
- 二.使用snakeyaml解析YAML文件
- 1.添加maven依赖
- 2.将Yaml文件解析成JavaBean
- 3.根据JavaBean生成Yaml
- 4.DumperOptions的FlowStyle属性
- 5.DumperOptions的ScalarStyle属性
- 三.使用jackson解析YAML文件
- 1.添加maven依赖
- 2.将Yaml文件解析成JavaBean
- 3.根据JavaBean生成Yaml
- 四.使用SpringBoot解析YAML文件
- 1.Environment
- 2.YamlPropertiesFactoryBean
- 3.监听事件
- 五.SpringBoot是如何解析yaml文件的
一.前言
YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便。类似于XML,JSON
。SpringBoot就支持yaml文件作为配置文件。
1.语法
- 大小写敏感
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格。
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- #表示注释,从这个字符一直到行尾,都会被解析器忽略。
2.数据结构
2.1.对象
- 写在一行
address: {province: 山东, city: 济南}
- 写在多行
address:
province: 山东
city: 济南
2.2.数组
- 写在一行
hobbyList: [游泳, 跑步]
- 写在多行
hobbyList:
- 游泳
- 跑步
2.3.普通属性
2.3.1.字符串
1.默认不用加引号
,包含空格或特殊字符必须加引号,单引号或双引号都可以
userId: S123
username: "lisi"
password: '123456'
province: 山东
city: "济南 : ss"
2.单引号和双引号都可以使用,双引号不会对特殊字符转义
。
s1: '内容\n字符串'
s2: "内容\n字符串"
等同于下面代码
{ s1: '内容\\n字符串', s2: '内容\n字符串' }
3.单引号之中如果还有单引号,必须连续使用2个单引号转义。
str: 'labor''s day'
等同于下面代码
str: 'labor\'s day' }
4.字符串可以写成多行
,从第2行
开始,必须有一个单空格
缩进。换行符会被转为空格
。
str: 这是一段
多行
字符串
等同于下面代码
{ str: '这是一段 多行 字符串' }
5.多行字符串可以使用 |
保留换行符,也可以使用>
折叠换行。
this: |
Foo
Bar
that: >
Foo
Bar
等同于下面代码
{ this: 'Foo\nBar\n', that: 'Foo Bar\n' }
6.+
表示保留文字块末尾的换行,-
表示删除字符串末尾的换行。
s1: |
Foo
s2: |+
Foo
s3: |-
Foo
等同于下面代码
{ s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' }
2.3.1.布尔值
布尔值用true
和false
表示。
success: true
2.3.2.整数
age: 13
2.3.3.浮点数
weight: 75.5
2.3.4.Null
用~
表示
gender: ~
2.3.5.日期、时间
时间使用ISO8601标准
createDate: 2001-12-14T21:59:43.10+05
日期采用复合 ISO8601格式的年、月、日表示。
date: 1976-07-31
2.4.强制转换数据类型
- yaml允许使用两个感叹号,强制转换数据类型。
e: !!str 123
f: !!str true
2.5.引用
锚点&
和 别名*
,可以用来引用。
defaults: &defaults
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults
test:
database: myapp_test
<<: *defaults
等同于下面的代码
defaults:
adapter: postgres
host: localhost
development:
database: myapp_development
adapter: postgres
host: localhost
test:
database: myapp_test
adapter: postgres
host: localhost
&
用来建立锚点(defaults)
,<<
表示 合并到当前数据
,*
用来引用锚点
。
- &showell Steve
- Clark
- Brian
- Oren
- *showell
等同于下面的代码
[ 'Steve', 'Clark', 'Brian', 'Oren', 'Steve' ]
二.使用snakeyaml解析YAML文件
1.添加maven依赖
snakeyaml支持从String或InputStream加载文档,我们从定义一个简单的YAML文档开始,然后将文件命名为User.yaml:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.28</version>
</dependency>
2.将Yaml文件解析成JavaBean
yaml文件,既可以.yaml结尾,也可以.yml结尾,支持两种方式的扩展名。
User.yaml
# 普通属性
userId: 1
username: lisi
password: 123456
# 对象
address: {province: 山东, city: "济南"}
# 字符串集合
hobbyList: [游泳, 跑步]
# 对象集合
phoneList: [{operator: 联通, number: 17603050528},{province: 移动, city: 13612359876}]
或
# 普通属性
userId: 1
username: "lisi"
password: '123456'
# 对象
address:
province: 山东
city: "济南"
# 字符串集合
hobbyList:
- 游泳
- 跑步
# 对象集合
phoneList:
- operator: "联通"
number: "17603050528"
- operator: "移动"
number: "13612359876"
User.yaml对应的JavaBean
@Data
public class User {
private String userId;
private String username;
private String password;
private Address address;
private List<String> hobbyList;
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class Address {
private String province;
private String city;
}
}
测试代码
public class YamlTest1 {
public static void main(String[] args) {
//获取classpath下面的User.yaml文件
InputStream resource = YamlTest1.class.getClassLoader().getResourceAsStream("/User.yaml");
if (Objects.nonNull(resource)) {
Yaml yaml = new Yaml();
//Yaml转JavaBean
User user = yaml.loadAs(resource, User.class);
System.out.println(user.getClass());
System.out.println(user);
}
}
}
执行结果
class com.demo.yaml.User
User(userId=1, username=lisi, password=123456, address=User.Address(province=山东, city=济南), hobbyList=[游泳, 跑步], phoneList=[User.Phone(operator=联通, number=17603050528), User.Phone(operator=移动, number=13612359876)])
- load()方法返回一个Map<String, Object>
- load()方法返回一个指定类型的JavaBean
3.根据JavaBean生成Yaml
public class YamlTest2 {
public static void main(String[] args) {
User user = new User();
user.setUserId("1");
user.setUsername("lisi");
user.setPassword("123456");
user.setAddress(new User.Address("山东", "济南"));
user.setHobbyList(Arrays.asList("游泳", "跑步"));
user.setPhoneList(Arrays.asList(new User.Phone("联通","17603050528"),new User.Phone("移动","13612359876")));
Yaml yaml = new Yaml();
//JavaBean转Yaml
String userString = yaml.dump(user);
System.out.println(userString);
//Yaml转JavaBean
System.out.println(yaml.loadAs(userString, User.class));
}
}
输出结果为
!!com.demo.yaml.User
address: {city: 济南, province: 山东}
hobbyList: [游泳, 跑步]
password: '123456'
phoneList:
- {number: '17603050528', operator: 联通}
- {number: '13612359876', operator: 移动}
userId: '1'
username: lisi
User(userId=1, username=lisi, password=123456, address=User.Address(province=山东, city=济南), hobbyList=[游泳, 跑步], phoneList=[User.Phone(operator=联通, number=17603050528), User.Phone(operator=移动, number=13612359876)])
上面的对象和数组是显示在一行
,我们也可以通过自定义序列化显示为多行
public class YamlTest3 {
public static void main(String[] args) {
User user = new User();
user.setUserId("1");
user.setUsername("lisi");
user.setPassword("123456");
user.setAddress(new User.Address("山东", "济南"));
user.setHobbyList(Arrays.asList("游泳", "跑步"));
user.setPhoneList(Arrays.asList(new User.Phone("联通","17603050528"),new User.Phone("移动","13612359876")));
//序列化参数
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
String userString = yaml.dump(user);
System.out.println(userString);
System.out.println(yaml.loadAs(userString, User.class));
}
}
执行结果
!!com.demo.yaml.User
address:
city: 济南
province: 山东
hobbyList:
- 游泳
- 跑步
password: '123456'
phoneList:
- number: '17603050528'
operator: 联通
- number: '13612359876'
operator: 移动
userId: '1'
username: lisi
User(userId=1, username=lisi, password=123456, address=User.Address(province=山东, city=济南), hobbyList=[游泳, 跑步], phoneList=[User.Phone(operator=联通, number=17603050528), User.Phone(operator=移动, number=13612359876)])
4.DumperOptions的FlowStyle属性
DumperOptions主要是用设置JavaBean转Yaml时的输出格式
DumperOptions.FlowStyle有3种格式
- FLOW
- BLOCK
- AUTO
设置方法
//序列化参数
DumperOptions dumperOptions = new DumperOptions();
//设置FlowStyle
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//设置ScalarStyle
dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
Yaml yaml = new Yaml(dumperOptions);
String userString = yaml.dump(user);
DumperOptions.FlowStyle.BLOCK
!!com.demo.yaml.User
address:
city: 济南
province: 山东
hobbyList:
- 游泳
- 跑步
password: '123456'
phoneList:
- number: '17603050528'
operator: 联通
- number: '13612359876'
operator: 移动
userId: '1'
username: lisi
DumperOptions.FlowStyle.FLOW
!!com.demo.yaml.User {address: {city: 济南, province: 山东}, hobbyList: [游泳, 跑步], password: '123456',
phoneList: [{number: '17603050528', operator: 联通}, {number: '13612359876', operator: 移动}],
userId: '1', username: lisi}
DumperOptions.FlowStyle.AUTO
!!com.demo.yaml.User
address: {city: 济南, province: 山东}
hobbyList: [游泳, 跑步]
password: '123456'
phoneList:
- {number: '17603050528', operator: 联通}
- {number: '13612359876', operator: 移动}
userId: '1'
username: lisi
5.DumperOptions的ScalarStyle属性
DumperOptions.ScalarStyle有4种格式
- DOUBLE_QUOTED
- SINGLE_QUOTED
- LITERAL
- PLAIN(null);
DumperOptions.ScalarStyle.DOUBLE_QUOTED: 双引号
!!com.demo.yaml.User
"address":
"city": "济南"
"province": "山东"
"hobbyList":
- "游泳"
- "跑步"
"password": "123456"
"phoneList":
- "number": "17603050528"
"operator": "联通"
- "number": "13612359876"
"operator": "移动"
"userId": "1"
"username": "lisi"
DumperOptions.ScalarStyle.SINGLE_QUOTED: 单引号
!!com.demo.yaml.User
'address':
'city': '济南'
'province': '山东'
'hobbyList':
- '游泳'
- '跑步'
'password': '123456'
'phoneList':
- 'number': '17603050528'
'operator': '联通'
- 'number': '13612359876'
'operator': '移动'
'userId': '1'
'username': 'lisi'
**DumperOptions.ScalarStyle.LITERAL: 竖线 | **
"address":
"city": |-
济南
"province": |-
山东
"hobbyList":
- |-
游泳
- |-
跑步
"password": |-
123456
"phoneList":
- "number": |-
17603050528
"operator": |-
联通
- "number": |-
13612359876
"operator": |-
移动
"userId": |-
1
"username": |-
lisi
DumperOptions.ScalarStyle.PLAIN(无)
!!com.demo.yaml.User
address:
city: 济南
province: 山东
hobbyList:
- 游泳
- 跑步
password: '123456'
phoneList:
- number: '17603050528'
operator: 联通
- number: '13612359876'
operator: 移动
userId: '1'
username: lisi
三.使用jackson解析YAML文件
1.添加maven依赖
jackson-dataformat-yaml是在snakeyaml的基础上又封装了一层。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.12.0</version>
</dependency>
2.将Yaml文件解析成JavaBean
public class YamlTest4 {
public static void main(String[] args) throws IOException {
InputStream resource = YamlTest4.class.getClassLoader().getResourceAsStream("User.yaml");
if (Objects.nonNull(resource)) {
YAMLMapper yamlMapper = new YAMLMapper();
//Yaml转JavaBean
User user = yamlMapper.readValue(resource, User.class);
System.out.println(user.getClass());
System.out.println(user);
}
}
}
执行结果
class com.demo.yaml.User
User(userId=1, username=lisi, password=123456, address=User.Address(province=山东, city=济南), hobbyList=[游泳, 跑步], phoneList=[User.Phone(operator=联通, number=17603050528), User.Phone(operator=移动, number=13612359876)])
3.根据JavaBean生成Yaml
public class YamlTest5 {
public static void main(String[] args) throws JsonProcessingException {
User user = new User();
user.setUserId("1");
user.setUsername("lisi");
user.setPassword("123456");
user.setAddress(new User.Address("山东", "济南"));
user.setHobbyList(Arrays.asList("游泳", "跑步"));
user.setPhoneList(Arrays.asList(new User.Phone("联通","17603050528"),new User.Phone("移动","13612359876")));
YAMLMapper yamlMapper = new YAMLMapper();
//JavaBeam转Yaml
System.out.println(yamlMapper.writeValueAsString(user));
}
}
执行结果
---
userId: "1"
username: "lisi"
password: "123456"
address:
province: "山东"
city: "济南"
hobbyList:
- "游泳"
- "跑步"
phoneList:
- operator: "联通"
number: "17603050528"
- operator: "移动"
number: "13612359876"
四.使用SpringBoot解析YAML文件
1.Environment
在Spring中有一个类Environment
,它可以被认为是当前应用程序正在运行的环境
,它继承了PropertyResolver接口
,因此可以作为一个属性解析器使用。先创建一个yml文件,属性如下
person:
name: hydra
gender: male
age: 18
使用起来也非常简单,直接使用@Autowired
就可以注入到要使用的类中,然后调用它的getProperty()
方法就可以根据属性名称取出对应的值了。
@RestController
public class EnvironmentController {
@Autowired
private Environment environment;
@GetMapping("envTest")
private void getEnv(){
System.out.println(environment.getProperty("person.name"));
System.out.println(environment.getProperty("person.gender"));
Integer autoClose = environment .getProperty("person.age", Integer.class);
System.out.println(autoClose);
String defaultValue = environmentgetProperty("person.other", String.class, "defaultValue");
System.out.println(defaultValue);
}
}
执行结果
hydra
male
18
defaultValue
2.YamlPropertiesFactoryBean
person2:
name: susan
gender: female
age: 18
- 在Spring中还可以使用
YamlPropertiesFactoryBean
来读取自定义配置的yml文件,而不用再被拘束于application.yml
及其激活的其他配置文件。
- 在使用过程中,只需要通过
setResources()
方法设置自定义yml配置文件的路径
,再通过getObject()
方法获取Properties对象
,后续就可以通过它获取具体的属性,下面看一个例子:
@GetMapping("fcTest")
public void ymlProFctest(){
YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();
yamlProFb.setResources(new ClassPathResource("application2.yml"));
Properties properties = yamlProFb.getObject();
System.out.println(properties.get("person2.name"));
System.out.println(properties.get("person2.gender"));
System.out.println(properties.toString());
}
执行结果
susan
female
{person2.age=18, person2.gender=female, person2.name=susan}
如何通过@Value获取自定义yaml中的配置
@Value("${person2.name:null}")
private String name;
@Value("${person2.gender:null}")
private String gender;
@GetMapping("fcTest2")
public void ymlProFctest2(){
System.out.println(name);
System.out.println(gender);
}
执行结果
null
null
解决方案
- 配合
PropertySourcesPlaceholderConfigurer
使用,它实现了BeanFactoryPostProcessor接口
,也就是一个bean工厂后置处理器的实现,可以将配置文件的属性值加载到一个Properties文件中
。使用方法如下:
@Configuration
public class PropertyConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();
yamlProFb.setResources(new ClassPathResource("application2.yml"));
configurer.setProperties(yamlProFb.getObject());
return configurer;
}
}
- 再次调用fcTest2口,结果如下,可以正常的取到application2.yml中的属性:
susan
female
除了使用YamlPropertiesFactoryBean
将yml解析成Properties
外,其实我们还可以使用YamlMapFactoryBean解析yml成为Map
,使用方法非常类似:
@GetMapping("fcMapTest")
public void ymlMapFctest(){
YamlMapFactoryBean yamlMapFb = new YamlMapFactoryBean();
yamlMapFb.setResources(new ClassPathResource("application2.yml"));
Map<String, Object> map = yamlMapFb.getObject();
System.out.println(map);
}
3.监听事件
SpringBoot是通过监听事件
的方式来加载和解析的yml文件,那么我们也可以仿照这个模式,来加载自定义的配置文件。
1.定义一个类实现ApplicationListener接口
,监听的事件类型为ApplicationEnvironmentPreparedEvent
,并在构造方法中传入要解析的yml文件
名:
2.自定义的监听器中需要实现接口的onApplicationEvent()
方法,当监听到ApplicationEnvironmentPreparedEvent事件时会被触发:
//yaml文件路径
private String ymlFilePath;
public YmlListener(String ymlFilePath) {
this.ymlFilePath = ymlFilePath;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
//获取当前环境Environment,当ApplicationEnvironmentPreparedEvent事件被触发时,已经完成了Environment的装载,并且能够通过event事件获取
ConfigurableEnvironment environment = event.getEnvironment();
ResourceLoader loader = new DefaultResourceLoader();
//通过YamlPropertySourceLoader加载、解析配置文件
YamlPropertySourceLoader ymlLoader = new YamlPropertySourceLoader();
try {
List<PropertySource<?>> sourceList = ymlLoader.load(ymlFilePath, loader.getResource(ymlFilePath));
//将解析完成后的OriginTrackedMapPropertySource添加到Environment中
for (PropertySource<?> propertySource : sourceList) {
environment.getPropertySources().addLast(propertySource);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的代码中,主要实现了:
- 获取当前环境
Environment
,当ApplicationEnvironmentPreparedEvent事件被触发时,已经完成了Environment的装载,并且能够通过event事件
获取 - 通过
YamlPropertySourceLoader
加载、解析配置文件 - 将解析完成后的
OriginTrackedMapPropertySource
添加到Environment
中
3.修改启动类,在启动类中加入这个监听器:
public static void main(String[] args) {
SpringApplication application = new SpringApplication (MyApplication.class);
application.addListeners(new YmlListener("classpath:/application2.yml"));
application.run(args);
}
4.启动springBoot项目,再次调用fcTest2口,结果如下,可以正常的取到application2.yml中的属性:
susan
female
五.SpringBoot是如何解析yaml文件的
参考文章