初始化(可选)

零配置启动

// 无需显式初始化——首次读取配置时自动以默认选项懒加载。
String host = Vostok.Config.get("db.host");

显式初始化

// 以内置默认选项初始化(幂等,已初始化则忽略)
Vostok.Config.init();

// 自定义选项
Vostok.Config.init(new VKConfigOptions()
    .watchEnabled(true)             // 开启文件热监听
    .watchDebounceMs(500)           // 防抖延迟(ms,最小 50)
    .loadEnv(true)                 // 加载环境变量(默认已开启)
    .loadSystemProperties(true)    // 加载 JVM 系统属性(默认已开启)
    .scanClasspath(true)           // 扫描 classpath(默认已开启)
    .scanUserDir(true)             // 扫描 user.dir(默认已开启)
    .addScanDir(Path.of("/etc/myapp"))  // 额外扫描目录
);
幂等初始化
init() 已初始化时静默忽略。如需强制替换配置,使用 reinit(VKConfigOptions)。 运行时修改选项(不替换)使用 configure(Consumer<VKConfigOptions>),会触发一次重载。

读取配置

基础读取

// 读取字符串(未找到返回 null)
String host = Vostok.Config.get("db.host");

// 必须存在,未找到抛 VKConfigException(KEY_NOT_FOUND)
String url = Vostok.Config.required("db.url");

// 读取字符串,含默认值
String host = Vostok.Config.getString("db.host", "localhost");

// 读取各类型(含默认值)
int     port    = Vostok.Config.getInt("db.port", 5432);
long    timeout = Vostok.Config.getLong("conn.timeoutMs", 3000L);
double  ratio   = Vostok.Config.getDouble("pool.factor", 0.75);
boolean enabled = Vostok.Config.getBool("feature.enabled", false);

// 读取列表(逗号分隔,或 key[0]/key[1] 数组格式)
List<String> hosts = Vostok.Config.getList("db.hosts");

// 检查 key 是否存在
boolean exists = Vostok.Config.has("db.host");

// 获取所有已加载的 key
Set<String> allKeys = Vostok.Config.keys();

getBool() 识别规则:

值(不区分大小写)结果
true / 1 / yes / ontrue
false / 0 / no / offfalse
其他值 / 未找到返回默认值

列表格式

getList(key) 支持两种配置格式:

# 格式 1:逗号分隔
db.hosts=host1,host2,host3

# 格式 2:下标数组
db.hosts[0]=host1
db.hosts[1]=host2
db.hosts[2]=host3

插值语法

配置值中可用 ${key} 引用其他配置项,支持默认值语法 ${key:defaultValue}。插值递归解析,循环引用时抛 VKConfigException

# application.properties
base.url=https://api.example.com
retry.url=${base.url}/retry

# 引用环境变量,缺省 5000
timeout=${REQUEST_TIMEOUT:5000}

# 多级引用
app.name=MyApp
log.prefix=[${app.name}]
插值解析时机
插值在每次 get() / getString() 等读取时实时解析,不在加载时展开。因此 runtime override 修改了引用目标后,读取结果会立即反映新值。

配置优先级

从低到高,高优先级覆盖低优先级:

  1. classpath 配置文件(scanClasspath=true
  2. addScanDir() 指定的额外扫描目录
  3. user.dir(工作目录,scanUserDir=true
  4. 手动追加的文件(addFile() / addFiles()
  5. 环境变量(loadEnv=true
  6. JVM 系统属性(loadSystemProperties=true
  7. 运行时覆盖(putOverride()
环境变量 key 规范化
环境变量 UPPER_CASE 会映射为 upper.case,两种形式同时可用。 系统属性中的 - 会规范化为 .

运行时覆盖

// 写入运行时覆盖(最高优先级,立即生效)
Vostok.Config.putOverride("db.url", "jdbc:postgresql://prod-host/mydb");

// 传入 null 等同于 removeOverride
Vostok.Config.putOverride("db.url", null);

// 移除单个覆盖
Vostok.Config.removeOverride("db.url");

// 清空所有覆盖
Vostok.Config.clearOverrides();

手动追加配置文件

// 追加单个文件(立即加载,触发一次重载)
Vostok.Config.addFile("/etc/myapp/secrets.properties");

// 追加多个文件(全部校验通过后统一触发一次重载)
Vostok.Config.addFiles("/conf/a.yml", "/conf/b.yml");

// 移除所有手动追加的文件(触发重载)
Vostok.Config.clearManualFiles();

// 手动触发全量重载
Vostok.Config.reload();

文件变更监听

开启 watchEnabled(true) 后,Config 模块自动监听文件系统变更,防抖后触发重载并通知监听器。

addChangeListener — 全局监听器

VKConfigChangeListener listener = event -> {
    // changedKeys():本次变更的所有 key(新增 / 修改 / 删除)
    if (event.changedKeys().contains("db.url")) {
        String oldUrl = event.oldValue("db.url");  // 变更前的值(新增时为 null)
        String newUrl = event.newValue("db.url");  // 变更后的值(删除时为 null)
        reconnect(newUrl);
    }
};

Vostok.Config.addChangeListener(listener);
Vostok.Config.removeChangeListener(listener);  // 注销

onChange — 单 key 快捷监听

// 仅在指定 key 变更时触发,返回 listener 实例供后续注销
VKConfigChangeListener l = Vostok.Config.onChange("server.port", (oldVal, newVal) -> {
    System.out.println("port: " + oldVal + " → " + newVal);
});

// 注销时复用返回的 listener 引用
Vostok.Config.removeChangeListener(l);

VKConfigChangeEvent 完整方法:

方法返回值说明
changedKeys()Set<String>本次变更的所有 key(新增 / 修改 / 删除)
oldValue(key)String变更前的值;key 是新增时返回 null
newValue(key)String变更后的值;key 被删除时返回 null
oldSnapshot()Map<String,String>变更前的完整配置快照(只读)
newSnapshot()Map<String,String>变更后的完整配置快照(只读)

配置校验

通过 VKConfigValidators 使用内置校验器,或实现 VKConfigValidator 接口自定义。每次重载后校验器会重新执行,失败时抛 VKConfigException(VALIDATION_ERROR)。

内置校验器

// 必填:key 必须存在且不为空白
Vostok.Config.registerValidator(VKConfigValidators.required("db.url", "db.port"));

// 整数范围:[min, max] 闭区间
Vostok.Config.registerValidator(VKConfigValidators.intRange("pool.size", 1, 200));

// 正整数(> 0)
Vostok.Config.registerValidator(VKConfigValidators.positiveInt("thread.count"));

// 端口范围:[1, 65535]
Vostok.Config.registerValidator(VKConfigValidators.portRange("server.port"));

// 枚举值(不区分大小写)
Vostok.Config.registerValidator(VKConfigValidators.oneOf("log.level", "TRACE", "DEBUG", "INFO", "WARN", "ERROR"));

// 正则格式(全匹配)
Vostok.Config.registerValidator(VKConfigValidators.pattern("app.version", "\\d+\\.\\d+\\.\\d+"));

// URL 格式(http / https 及自定义 scheme)
Vostok.Config.registerValidator(VKConfigValidators.url("api.baseUrl"));

跨字段校验

// cross:带名称的自定义谓词,可访问完整配置视图
Vostok.Config.registerValidator(VKConfigValidators.cross(
    "ssl-consistency",
    view -> !view.getBool("ssl.enabled", false) || view.has("ssl.certPath"),
    "ssl.certPath is required when ssl.enabled=true"
));

自定义校验器

Vostok.Config.registerValidator(view -> {
    String url = view.get("db.url");
    if (url == null || !url.startsWith("jdbc:")) {
        throw new IllegalStateException("db.url must be a valid JDBC URL");
    }
});

// 移除所有校验器
Vostok.Config.clearValidators();

VKConfigView(校验器收到的视图)方法:

方法说明
get(key)读取字符串,未找到返回 null
has(key)检查 key 是否存在
keys()所有 key 集合
asMap()完整配置快照(只读 Map)
getInt(key, default)解析整数
getLong(key, default)解析 long
getDouble(key, default)解析 double
getBool(key, default)解析布尔
getList(key)解析列表(逗号分隔或下标格式)

类型安全绑定

将配置项自动绑定到 POJO 字段,支持 String、基本类型及其包装类、List<String>。POJO 需提供无参构造器。

使用 @VKConfigPrefix 注解

// 在 POJO 类上标注前缀
@VKConfigPrefix("database")
public class DatabaseConfig {
    private String  host;          // ← database.host
    private int     port;          // ← database.port
    private boolean sslEnabled;    // ← database.sslEnabled 或 database.ssl-enabled
    private List<String> replicas;  // ← database.replicas(逗号分隔)
}

// 绑定(通过注解自动取前缀)
DatabaseConfig cfg = Vostok.Config.bind(DatabaseConfig.class);

显式指定前缀

// 不需要注解,直接传入前缀字符串
public class RedisConfig {
    private String host;
    private int    port;
    private int    maxConnections;  // ← redis.maxConnections 或 redis.max-connections
}

RedisConfig cfg = Vostok.Config.bind("redis", RedisConfig.class);
字段名映射规则
每个字段按以下顺序尝试匹配(找到即停止):
  1. prefix.fieldName(camelCase 原样)
  2. prefix.field-name(camelCase → kebab-case 转换)

支持的字段类型

字段类型说明
String直接赋值
int / Integer解析为整数
long / Long解析为 long
double / Double解析为 double
float / Float解析为 float
boolean / BooleangetBool() 规则
List<String>getList(),逗号分隔或下标格式

自定义解析器

实现 VKConfigParser 接口可支持任意文件格式(如 TOML、JSON、HOCON 等)。解析器注册后优先级最高,同时触发一次重载。

Vostok.Config.registerParser(new VKConfigParser() {

    @Override
    public boolean supports(String fileName) {
        return fileName.endsWith(".toml");
    }

    @Override
    public Map<String, String> parse(String sourceId, InputStream inputStream) {
        // 解析文件内容,返回扁平化的 key-value Map
        // sourceId 为来源标识(文件路径或 jar 内路径),可用于错误提示
        Map<String, String> result = new LinkedHashMap<>();
        // ... 解析逻辑 ...
        return result;
    }
});
内置格式
.properties(Java Properties 格式)和 .yml / .yaml(YAML,支持嵌套对象展平、序列、行内注释、引号值)无需注册,开箱即用。

来源追踪

用于调试排查:某个 key 当前读取的值来自哪个文件(或环境变量、系统属性、运行时覆盖)。

// 查询单个 key 的来源
String source = Vostok.Config.sourceOf("db.url");
// 可能的返回值示例:
//   "/home/app/application.properties"  — 文件路径
//   "env"                                — 环境变量
//   "system-property"                    — JVM 系统属性
//   "runtime-override"                   — putOverride() 写入
//   null                                 — key 不存在

// 获取全部 key 的来源映射(只读快照)
Map<String, String> allSources = Vostok.Config.sources();

文件监听状态

// 是否已初始化(或已懒加载)
boolean ready = Vostok.Config.started();

// 上次文件监听出错时的错误信息(无错误时返回 null)
// 文件监听出错时保留旧配置快照,不影响读取
String watchErr = Vostok.Config.lastWatchError();

生命周期控制

// 以内置默认选项初始化(幂等)
Vostok.Config.init();

// 强制重新初始化(忽略已初始化状态)
Vostok.Config.reinit(new VKConfigOptions().watchEnabled(true));

// 修改当前选项(触发一次重载,不重置 listeners / validators / parsers)
Vostok.Config.configure(opts -> opts.watchDebounceMs(200));

// 手动触发全量重载
Vostok.Config.reload();

// 关闭(停止文件监听,清空所有状态)
Vostok.Config.close();

配置参数(VKConfigOptions)

参数类型默认值说明
扫描源
scanClasspathbooleantrue扫描 classpath 下的配置文件(JAR 中最多扫描一层)
scanUserDirbooleantrue扫描 user.dir(工作目录)下的配置文件(递归)
addScanDir(Path)添加额外扫描目录(递归扫描),可多次调用;通过 getExtraScanDirs() 获取列表
外部来源
loadEnvbooleantrue加载环境变量(UPPER_CASE → upper.case,两种形式均可用)
loadSystemPropertiesbooleantrue加载 JVM 系统属性(- 规范化为 .
envProviderSupplier<Map>System::getenv自定义环境变量来源(测试时常用于注入 mock 环境)
systemPropertiesProviderSupplier<Properties>System::getProperties自定义系统属性来源
热监听
watchEnabledbooleanfalse开启文件系统监听,文件变更后自动重载配置
watchDebounceMslong300文件变更防抖延迟(ms),最小 50ms
冲突控制
strictNamespaceConflictbooleanfalse开启后,同一命名空间出现在多个来源时抛异常(严格模式)

异常说明

错误码触发场景
CK-404 KEY_NOT_FOUNDrequired(key) 读取不存在的 key
CK-420 VALIDATION_ERROR校验器执行失败(含内置与自定义)
CK-400 INVALID_ARGUMENT传入参数非法(如空 key、空路径)
CK-402 CONFIG_ERROR插值循环引用等配置逻辑错误
CK-500 IO_ERROR文件读取失败
CK-510 PARSE_ERROR文件格式解析失败

API 速查

生命周期

方法返回值说明
init()void以默认选项初始化(幂等)
init(VKConfigOptions)void以自定义选项初始化(幂等)
reinit(VKConfigOptions)void强制重新初始化
configure(Consumer<VKConfigOptions>)void修改当前选项并触发重载
reload()void手动触发全量重载
close()void关闭并重置所有状态
started()boolean是否已初始化(含懒加载)
lastWatchError()String上次文件监听错误信息,null 表示无错误

读取

方法返回值说明
get(key)String读取字符串,未找到返回 null
required(key)String读取字符串,未找到抛 VKConfigException
has(key)boolean检查 key 是否存在
keys()Set<String>所有已加载的 key
getString(key, default)String读取字符串,含默认值
getInt(key, default)int解析整数,含默认值
getLong(key, default)long解析 long,含默认值
getDouble(key, default)double解析 double,含默认值
getBool(key, default)boolean解析布尔,含默认值
getList(key)List<String>解析列表(逗号分隔或下标格式)

运行时覆盖与文件管理

方法说明
putOverride(key, value)写入运行时覆盖(最高优先级),null value 等同 removeOverride
removeOverride(key)移除指定运行时覆盖
clearOverrides()清空所有运行时覆盖
addFile(path)手动追加单个配置文件并重载
addFiles(paths...)手动追加多个文件,统一触发一次重载
clearManualFiles()移除所有手动追加的文件并重载

监听器 / 校验器 / 解析器

方法说明
addChangeListener(VKConfigChangeListener)注册全局配置变更监听器
removeChangeListener(VKConfigChangeListener)注销监听器
onChange(key, BiConsumer<String,String>)单 key 变更快捷监听,返回 listener 实例
registerValidator(VKConfigValidator)注册校验器(每次重载后执行)
clearValidators()移除所有校验器
registerParser(VKConfigParser)注册自定义文件格式解析器(最高优先级)

绑定与来源追踪

方法返回值说明
bind(prefix, Class<T>)T将指定前缀的配置绑定到 POJO 实例
bind(Class<T>)T通过 @VKConfigPrefix 注解取前缀后绑定
sourceOf(key)String返回 key 的来源标识(文件路径 / "env" / "system-property" / "runtime-override" / null)
sources()Map<String,String>全部 key 的来源映射(只读快照)