Y服務-你真的懂 Yaml 嗎

在Java 的世界裏,配置的事情都交給了 Properties,要追溯起來這個模塊仍是從古老的JDK1.0 就開始了的。java

"天哪,這但是20年前的東西了,我竟然還在用 Properties.."正則表達式

然而,本文的主角並非Properties,而是Yaml。這是新時代裏微服務架構上的寵兒,和 Properties 相比起來,Yaml 顯得有些弄潮兒。
以往的大多數項目裏,咱們均可以發現 Properties配置文件的蹤影,這包括用做業務屬性配置的、機機接口交互的、國際化的等等用途。
而少許的一些狀況下,也存在一些"混合式"的作法,好比:spring

  • 使用 Xml 來表示一些模板
  • 使用一個 Json 格式化的字符串
  • 裸奔的文本格式,應用自解析
    ...

混雜的配置方式每每出如今一些充滿"壞味道"的項目裏頭,由於代碼陳舊、斯人已矣 等緣由,很難造成統一的方式。
然而,除開 Properties 屬性文件這種簡單的配置方式以外,採用其餘的方法不外乎都是爲了適應配置複雜、多元化的訴求。編程

那麼,Yaml 就是應對這種場景而產生的,在 SpringBoot 的官方文檔中,有很多篇幅是 使用了 Yaml 語法的配置格式。
下面介紹一下 Yaml 以及它是如何使用的。數組

1、什麼是 Yaml

來自百科的定義
"Yaml 是一個可讀性高,易用的數據序列化格式,由 Clark Evans 在2001年首次發表。"
可見 Yaml 並非一個很新的東西,只是在之前接觸的人很少罷了。此外,Yaml也被各類編程語言及框架所支持, 通用性很高。
在Java體系中,通常的微服務框架都支持甚至優先推薦使用 Yaml 做爲首選的配置語言。springboot

而 Yaml 自己具備什麼特色? 看看下面的一個實例:架構

environments:
    dev:
        url: https://dev.example.com
  name: Developer Setup
    prod:
        url: https://another.example.com
        name: My Cool App

這段語法等價的 Properties 爲:框架

environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App

可見, yaml 相對來講更加的結構化,更適合用來表達一個對象。
它在語法上有這樣的特色:dom

  • 大小寫敏感
  • 使用空格縮進表示層級關係,摒棄使用Tab鍵,這主要是考慮到不一樣平臺上文本展示時須要對齊
  • 縮進的空格數目不重要,只要相同層級的元素左側對齊便可
  • 使用 # 開頭做爲註釋行
  • 使用 鏈接符(-)開頭來描述數組元素

對比 Properties
Properties 能夠很好的實現 Key-Value 的配置,包括做爲一些國際化內容的配置方式。
但 Properties 很難表現多層級的嵌套關係,此時若是用 Yaml 能夠較好的彌補該短板。

對比 Json
Yaml 與 Json自己沒有太多的優劣之分,二者都是結構化的表達式語言,可是Json的設計重點在於簡單易用、方便傳輸的特性;
而 Yaml 則側重於可讀性(更加在意外觀),幾乎能夠把 Yaml 看作是 Json 的一個"超集",便可讀性更高(更漂亮) 的結構化格式。
此外,Json更加便於生成和解析,適合在各類跨語言、分佈式的環境中傳輸和交互;與此同時, Yaml 則通常只是用做的配置較多。

關於 Yaml 的定義能夠訪問下面的地址:
http://www.yaml.org/spec/1.2/spec.html

2、Yaml 的語法

Yaml 是很是簡單的, 它所定義的元素只有三個:

  • 對象:就是鍵值對的集合,對應於Java 中的 HashMap
  • 數組:指一組按序排列的值,對應於Java 中的 List
  • 單值:單個的、不可再分的值,好比 3,"Jackson"

對象如何表示
一個對象的屬性、嵌套關係經過空格縮進對齊來表示,以下:

article:
    title: 一我的的自白書
    author:
        name: 陳玲
        gender: female

數組如何表示
數組的元素經過鏈接符(-)來表示,以下:

article:
    title: 一我的的自白書
    tags:
        - 傳記
        - 社會
        - 人物

構成對象、數組內容的基本單元是單值,Yaml支持的單個值的類型有七種,以下:

類型 範例
字符串 Bob
布爾值 true
整數 199
浮點數 19.91
Null ~
時間 2001-12-14T22:14:09.10+08:00
日期 2019-01-09

其中,日期、時間使用的是 ISO 8601 國際標準格式,關於它的定義能夠參考:
https://www.w3.org/TR/NOTE-datetime

通常狀況下單個值會在一行內結束。但若是遇到多行的字符串,可使用一些特殊字符表示,
好比:

text: |
  Hello
  World

對應的結果爲:

{ text: 'Hello\nWorld\n' }

能夠用+表示保留字符串末尾的換行,-表示刪除字符串末尾的換行:

text1: |+
  Hello
  

text2: |-
  Hello

對應的結果爲:

{ text1: 'Hello\n\n\n', text2: 'Hello' }

除此以外,Yaml 還能夠支持引用、函數、正則表達式等高級用法,但項目上通常不多用到。

3、操做 Yaml

目前用來操做 Yaml 的經常使用組件是 Snake Yaml,這個庫支持標準的 Yaml 1.1 版本

SpringBoot 官方文檔也介紹了整合該框架的方式,參考下面的地址:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-loading-yaml

下面提供 將SnakeYaml 整合到項目的樣例。

A. 引入框架

在Maven的pom.xml文件中添加:

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.21</version>
</dependency>

B. 代碼片斷

實現加載配置文件

以下面的代碼,實現了從類路徑config.yml文件中加載 yaml 配置內容:

InputStream inputStream = YamlUtil.class.getClassLoader()
        .getResourceAsStream("config.yml");

Yaml yaml = new Yaml();
Map<String, Object> objectMap = yaml.load(inputStream);
System.out.println(objectMap.get("path"));

實現對象轉換

定義以下的Pojo 對象:

public static class A{
    private String name = "hello";
    private List<B> bs = new ArrayList<B>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<B> getBs() {
        return bs;
    }

    public void setBs(List<B> bs) {
        this.bs = bs;
    }
}

public static class B{
    private String id = UUID.randomUUID().toString();

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

經過 SnakeYaml 將對象輸出爲 Yaml 格式的代碼:

A a = new A();
a.getBs().add(new B());
a.getBs().add(new B());

Yaml yaml = new Yaml();
String aString = yaml.dumpAsMap(a);
System.out.println(aString);

輸出結果以下:

bs:
- id: b3688f05-ea7e-436b-bc9a-9c5df555c7fd
- id: 7906224d-8ecc-43b8-bc3b-07985bc18ebd
name: hello

此時若是但願將Yaml 文本反過來轉換爲 A 對象,能夠執行下面的代碼:

A a1 = new Yaml().parseToObject(aString, A.class);
...

C. 完整案例

最終,咱們能夠將 Yaml 文檔的操做封裝爲一個工具類,方便在業務代碼中集成。

YamlUtil.java

public class YamlUtil {

    /**
     * 從資源文件加載內容,並解析爲Map對象
     *
     * @param path
     * @return
     */
    public static Map<String, Object> loadToMap(String path) {
        if (StringUtils.isEmpty(path)) {
            return Collections.emptyMap();
        }

        InputStream inputStream = YamlUtil.class.getClassLoader()
                .getResourceAsStream(path);

        Yaml yaml = new Yaml();
        Map<String, Object> objectMap = yaml.load(inputStream);
        return objectMap;
    }

    /**
     * 將字符串解析爲Map對象
     *
     * @param content
     * @return
     */
    public static Map<String, Object> parseToMap(String content) {
        if (StringUtils.isEmpty(content)) {
            return Collections.emptyMap();
        }

        Yaml yaml = new Yaml();
        Map<String, Object> objectMap = yaml.load(content);
        return objectMap;
    }

    /**
     * 將字符串解析爲類對象
     *
     * @param content
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parseToObject(String content, Class<T> clazz) {
        if (StringUtils.isEmpty(content) || clazz == null) {
            return null;
        }

        Yaml yaml = new Yaml(new Constructor(clazz));
        T object = yaml.load(content);
        return object;
    }

    /**
     * 格式化對象
     *
     * @param object
     * @return
     */
    public static String format(Object object) {
        Yaml yaml = new Yaml();
        return yaml.dumpAsMap(object);
    }

}

至此,咱們已經完成了 Yaml 的讀寫。固然,除了上述的Snake Yaml 以外,還可使用 流行的 Jackson 組件了進行解析,這裏再也不過多贅述,有興趣的朋友能夠自行嘗試。

參考文檔

阮一峯-YAML語言教程:
http://www.ruanyifeng.com/blog/2016/07/yaml.html

SnakeYaml 官方文檔:
https://bitbucket.org/asomov/snakeyaml/wiki/Documentation

Yaml 1.2 規範:
http://www.yaml.org/spec/1.2/spec.html

SpringBoot-LoadingYaml
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-loading-yaml

點擊查看美碼師的 SpringBoot 補習系列

相關文章
相關標籤/搜索