細數Java項目中用過的配置文件(YAML篇)
靈魂拷問:YAML,在項目中用過沒?它與 properties 文件啥區別?
目前 SpringBoot、SpringCloud、Docker 等各大項目、各大組件,在使用過程中幾乎都能看到 YAML 文件的身影。
2017 年的時候,我才真正把 YAML 文件用到負責的項目中,當時用 YAML 文件主要是為 Sharding-JDBC 配置數據源以及分庫分表的規則。
從實際項目中把 sharding-jdbc.yaml 文件抽出來,為了更清晰,進行了大量簡化,接下來就一同感受一下 YAML 的魅力。
1. 初步感受 YAML 的魅力。
上圖配置的內容雖然還沒解釋,仔細去看配置,大體都能看明白。其實,這就是 YAML 比 properties 配置文件的優勢所在,層次感分明,配置有序,而且比較簡潔。
縱然配置已經很清晰,還是要稍微帶著看一看配置內容。
dev 是一個對象,對應於 Java 中的 Map,包含 datasources 和 tables 兩個屬性。其本身含義是開發環境配置,當然實際項目中也會有測試、准生產、生產的對應的配置。
datasources 屬性是一個數組,對應於 Java 中的 List,數組元素由 name、default 等 10 個屬性構成。其本身含義是數據源配置,因為涉及到分庫,所以會有好多庫要連接,圖中只列舉兩個資料庫;
tables 屬性也是一個數組,對應於 Java 中的 List,數組元素由 tableName、databaseCount 等 5 個屬性構成。其本身含義是要拆分表的規則配置,圖中只列舉一個項目基本資訊表。
按照常規思路,寫好配置文件,接下來就要校驗一下,再稍微格式化一下。
在這兒校驗Yaml文件:http://www.bejson.com/validators/yaml/
如上圖所示,YAML 文件校驗轉換之後,就真的太清晰啦!
不過,YAML 是很簡單,但是有些細節,在開發中還是要注意,否則入坑就難跳出(一旦入坑,真的不好跳出來,別問為什麼?一個空格難倒英雄漢,真心體會過)。
Tips:
1. 使用冒號加縮進的方式代表層級關係,使用短橫杠代表數組元素;
2. 注意縮進不允許使用「tab」鍵,只能使用空格鍵(曾經掉這個坑啦,記憶之深刻);
3. 縮進空格個數多少並不重要,只要相同層級的元素左對齊即可;
4. 如果冒號後跟著 value,一定要注意冒號後跟上空格呦!
5. YAML 大小寫很敏感。
有關 YAML 的更多規範,可以參考如下 pdf,本次不過多展開去講。
https://yaml.org/spec/1.2/spec.pdf
2. YAML 配置有了,該怎麼去解析呢?
在不同的程式語言中,都有很多三方工具可以解析 YAML 文件,而在 Java 項目可以用 SnakeYaml 進行解析,接下來就寫寫程式碼體驗一下 yaml 文件的解析。
首先引入依賴包(想用人家,就別想撇清關係!)
<dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>1.18</version> </dependency>
碼點程式碼(從原項目中直接拿來,為了清晰,索性只留解析 YAML 文件部分的程式碼,呈現給你)
import org.apache.commons.collections4.MapUtils; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; /** * @author 一猿小講 */ public class ShardingJdbcDataSource { private static LinkedHashMap<String, Object> profile; @SuppressWarnings("unchecked") public static ArrayList<LinkedHashMap<String, Object>> parse(String profileId, String key) throws IOException { //如果profile為空的情況才進行載入配置文件,減少讀取文件的次數 if (MapUtils.isEmpty(profile)) { Yaml yaml = new Yaml(); File file = new File(ShardingJdbcDataSource.class.getResource("/").getPath() + "sharding-jdbc.yaml"); try (FileReader fileReader = new FileReader(file);) { Map<String, Object> result = yaml.loadAs(fileReader, Map.class); profile = (LinkedHashMap<String, Object>) result.get(profileId); } } return (ArrayList<LinkedHashMap<String, Object>>) profile.get(key); } public static void main(String[] args) throws IOException { // 開發環境 String profileId = "dev"; // 數據源集合 ArrayList<LinkedHashMap<String, Object>> datasources = parse(profileId, "datasources"); for (LinkedHashMap<String, Object> datasource : datasources) { System.out.println(String.format("資料庫名稱:%s",datasource.get("name"))); System.out.println(String.format("資料庫URL:%s",datasource.get("jdbc.url"))); } System.out.println("=============華麗的分割線=============="); // 表集合 ArrayList<LinkedHashMap<String, Object>> tables = parse(profileId, "tables"); for (LinkedHashMap<String, Object> table : tables) { System.out.println(String.format("表名稱:%s",table.get("tableName"))); System.out.println(String.format("資料庫拆分 %d 個",table.get("databaseCount"))); System.out.println(String.format("每個資料庫表的數目:%s",table.get("tableCountPerDatabase"))); } } }
去掉 main 函數,發現解析程式碼沒幾行,跑起來看一看,效果還可以。
文中的解析 YAML 文件的程式碼,改個類名,就可以直接變成工具類,如果有需要,自行簡單封裝一下就 ok 啦。
其中 SnakeYaml 類庫還有很多 API 可以使用,不一一帶著寫程式碼啦,感興趣的自行參考 SnakeYaml 官方文檔,去照貓畫虎敲敲吧。
https://bitbucket.org/asomov/snakeyaml/wiki/Documentation
另外,細心的你在平時研發時,有沒有發現,有的項目 YAML 文件的後綴是 .yml,有的項目卻是 .yaml,到底哪個是正確的呢?很久很久之前我也糾結過,感興趣可以去 stackoverflow 溜達一番。
https://stackoverflow.com/questions/21059124/is-it-yaml-or-yml
3. 它山之石可以攻玉
在 Java 項目研發過程中,總會遇到一些經常改變的參數,比如要連接的資料庫的連接地址、名稱、用戶名、密碼;再比如訪問三方服務的 URL 等等。考慮到程式的通用性,這些參數往往不能直接寫死在程式里,通常藉助配置文件來優雅處理,而常用的配置文件多數為 Properties,而相對 Properties 而言 YAML 卻表現的更加層次分明。
好了,有關 YAML 文件在實際項目中的使用,本次就談到這裡,它山之石可以攻玉,希望能對你有所幫助。