项目配置
.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());
}
}
});
}
}