項目配置
.NET中的項目配置文件主要是.config和.json文件;而java中則主要是.properties和.yml文件,一般放在resource目錄下,maven項目則通常放在resources項目下。
本地配置
properties
properties文件是java早期的配置文件格式,鍵值對格式,使用等號或冒號分隔,以“#”和“!”作爲註釋。
#名稱
app.id:1
app.name=示例項目
讀取:
Properties properties = new Properties();
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("app.properties");
//這裏是爲了防止中文亂碼
properties.load(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
System.out.println(properties.get("app.id"));
System.out.println(properties.get("app.name").toString().length());
我覺得初次上手最容易忽視的反而是空格的問題,雖然不起眼,但極有可能會帶來大麻煩。
yml
雖然key-value的配置方式在絕大多數的場景下已經夠用,但處理複雜的配置結構則有些繁瑣,這時候則可以選擇yaml,其結構更爲清晰,並支持列表、字典等複雜的數據結構,特性如下:
- 大小寫敏感,也就是說username和userName是兩個東西;
- 使用縮進表示層級關係,縮進不允許使用tab,只允許使用空格,縮進的空格數不重要,同層級的元素左對齊即可;
- 使用“#”表示註釋。
yaml支持三種數據類型:
純量
簡單變量,支持字符、布爾、整數、Null等。
id: 1 # 整數
name: 張三 # 字符串默認不用引號,單包含空格或特殊字符則必須加引號,單引號或雙引號均可
gender: ~ # Null
birthday: 2000-01-01T00:00:00.10+08 #ISO8601標準時間
對象
鍵值對集合,如:
user: {id: 1,name: 張三}
當然還有另外一種寫法:
user:
id: 1
name: 張三
數組
fruits: [ 蘋果,香蕉,橘子,梨,葡萄 ]
另一種寫法則是以“-”開頭,表示數組中的一項:
fruits:
- 蘋果
- 香蕉
- 橘子
- 梨
- 葡萄
結合對象的例子,我們展示一個user數組:
users:
- id: 1
name: 張三
- id: 2
name: 李四
讀取yml文件需要snakeyaml,maven依賴如下:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.2</version>
</dependency>
配置讀取代碼如下:
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("app.yml");
Yaml yaml = new Yaml();
Map<String, Object> map = yaml.loadAs(inputStream, Map.class);
map.forEach((String key, Object val) -> {
System.out.println("鍵:" + key + "\t值:" + val);
});
如果只使用純量的話,使用Map就夠了,但如果對象比較複雜,則需要轉爲爲javabean:
id: 1 # 整數
name: 張三 # 字符串默認不用引號,單包含空格或特殊字符則必須加引號,單引號或雙引號均可
gender: ~ # Null
birthday: 2000-01-01T00:00:00.10+08 #ISO8601標準時間
tags: [ 高, 富, 帥 ]
userDetail: { schoolName: 山河大學,className: 三年級1班 }
User.java
package em.im.pve;
import lombok.*;
import java.util.Date;
@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
int id;
String name;
String gender;
Date birthday;
String[] tags;
UserDetail userDetail;
}
UserDetail.java
package em.im.pve;
import lombok.*;
@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class UserDetail {
String schoolName;
String className;
}
Apollo客戶端
使用配置文件雖然可以非常靈活的修改配置,但業務分佈式部署時,尤其量級達到幾十上百臺時,變更配置極爲困難,也不便於管理,爲此,我們需要一個配置管理中心,集中管理。
官網:https://www.apolloconfig.com/#/zh/README
不過官方文檔寫的一言難盡,客戶端想正常使用首先需要引入Maven依賴:
<!--攜程Apollo-->
<dependencys>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-core</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
</dependencys>
如果引入不完整會拋出Provider com.ctrip.framework.apollo.internals.DefaultInjector could not be instantiated
異常。
Apollo配置:
app:
id: 14889
apollo:
meta: http://apollo-test.em/
cache-dir: D:\apolloconfigs\
autoUpdateInjectedSpringProperties: true #是否開啓Spring參數自動更新
env: UAT
bootstrap:
enabled: true #是否開啓Apollo
eagerLoad:
enabled: true #飢餓加載
access-key:
secret:
配置獲取:
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
/**
* 配置中心。
*/
public class ConfigContainer {
static Config config = ConfigService.getAppConfig();
private static HashSet<String> apiToken = null;
public static String getConfig(String key) {
return config.getProperty(key, "");
}
/**
* Apollo Change事件監聽。
*/
public static void openChangeListener() {
config.addChangeListener(new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent configChangeEvent) {
System.out.println("Change for namespace " + configChangeEvent.getNamespace());
for (String key : configChangeEvent.changedKeys()) {
ConfigChange change = configChangeEvent.getChange(key);
System.out.printf("Found change-key:%s,oldValue:%s,newValue:%s%n", change.getPropertyName(), change.getOldValue(), change.getNewValue());
}
}
});
}
}