文章目录

  • 一.前言
  • 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,JSONSpringBoot就支持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.布尔值

布尔值用truefalse表示。

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文件的

java yaml 语法检查 yaml java类_字符串


java yaml 语法检查 yaml java类_ci_02


参考文章