配置文件服务 ConfigService 可以将 Java 配置对象映射为插件数据目录中的 YAML 或 JSON 文件。配置结构、默认值和注释由代码声明,管理员可以在磁盘文件中修改实际值。
配置对象必须使用 @ConfigFile,并满足 文件服务 ⇱ 中的对象约束。
import api.linlang.file.file.FileType;
import api.linlang.file.file.annotations.Comment;
import api.linlang.file.file.annotations.ConfigFile;
import api.linlang.file.file.annotations.Key;
import api.linlang.file.file.annotations.NamingStyle;
@ConfigFile(name = "config", format = FileType.YAML)
@NamingStyle(NamingStyle.Style.KEBAB)
@Comment("插件主配置文件")
public class MainConfig {
@Comment("当前全局语言")
public String language = "zh_CN";
@Key("debug")
public boolean debugMode = false;
public Database database = new Database();
public static class Database {
public String host = "127.0.0.1";
public int port = 3306;
}
}
默认的 KEBAB 命名会把 debugMode 映射为 debug-mode,但字段上的 @Key("debug") 会覆盖自动命名。上例生成的 YAML 结构为:
# 插件主配置文件
# 当前全局语言
language: zh_CN
debug: false
database:
host: 127.0.0.1
port: 3306
@ConfigFile 有三个参数:
name:不含扩展名的文件名,默认为 config。path:相对于插件数据目录的子目录,默认为空。format:FileType.YAML 或 FileType.JSON,默认为 YAML。例如:
@ConfigFile(name = "database", path = "settings", format = FileType.JSON)
public class DatabaseConfig {
}
它对应:
plugins/<YourPlugin>/settings/database.json
通过 lin.linFile().config() 取得服务,再绑定配置类:
ConfigService configService = lin.linFile().config();
MainConfig config = configService.bind(MainConfig.class);
绑定时,服务会:
@ConfigFile 声明的路径和格式。提交与失败处理的详细流程见配置校验 ⇱。
返回对象可以直接读取和修改:
if (config.debugMode) {
getLogger().info("Debug mode enabled");
}
当已有配置缺少代码中声明的字段时,Linlang 会在内存文档中使用默认值补齐。允许写回且配置校验成功时,生成同名的 -diff 文件标记缺失路径,并更新原配置。
例如 config.yml 缺少字段时,旁边会出现:
config-diff.yml
差异文件用于提示管理员本次增加了哪些配置项,不应作为插件读取的主配置文件。
修改活动对象的公开字段后,调用 saveAll() 保存所有已绑定配置:
config.debugMode = true;
config.database.host = "db.example.com";
configService.saveAll();
保存用于持久化代码对活动对象的修改。若管理员已经修改了磁盘文件,不要在读取前无条件调用 saveAll(),否则可能先用内存值覆盖这些编辑。
磁盘配置被外部修改后,可以重新加载:
configService.reload();
reload() 会原地更新此前由该服务绑定的对象,不会返回新对象:
MainConfig config = configService.bind(MainConfig.class);
configService.reload();
String language = config.language;
reload() 以有效的磁盘内容更新活动对象,不会合并尚未保存的内存修改。应根据实际修改来源选择保存或重载。
同一配置类重复绑定会复用并刷新活动根对象,不会重复执行根对象构造方法。普通嵌套对象尽量原地更新;Map、List、数组和配置文本字段可以被替换,因此重载后应从活动对象重新获取它们。为 Map、List 中新建的配置对象仍需提供公开无参构造方法。
加载失败时保留该文件的旧活动值,其他文件仍会继续重载。失败结果、错误注释及保存保护统一见配置校验 ⇱。
lin.settings().apply() 只原地应用运行参数,不会重读配置或替换配置服务。原有 configService 与根配置对象继续有效。
只读取而不允许生成或保存文件时,可以在绑定时传入 false:
MainConfig config = configService.bind(MainConfig.class, false);
也可以在配置类上使用 @NoEmit:
@NoEmit
@ConfigFile(name = "external")
public class ExternalConfig {
}
两种方式都会禁止该绑定的磁盘写回,包括首次生成、缺失补齐和后续保存。读取和对象填充仍会执行。
配置对象可使用以下注解:
| 注解 | 作用 | ||
|---|---|---|---|
@ConfigFile | 声明文件名、子目录和格式 | ||
@NamingStyle | 设置类中字段的自动命名方式 | ||
@Key | 为单个字段指定文件键名 | ||
@Comment | 为 YAML 类或字段写入注释 | ||
@NoEmit | 禁止生成和写回文件 | ||
@ConfigVersion | 声明配置版本与版本键 |
@Comment 只影响 YAML 输出。JSON 格式不会保存注释。
可以使用 @ConfigVersion 在文件中记录结构版本:
@ConfigFile(name = "config")
@ConfigVersion(2)
public class MainConfig {
public String newName = "default";
}
默认版本键为 _linlang-version。读取旧版本配置时,运行时会按当前版本查找连续的 Migrator,先修改文档,再填充对象。如果迁移链缺失、重复或文件版本高于代码支持版本,绑定会失败,以避免在未知结构上继续运行。
迁移器应在绑定对应配置类之前注册:
configService.registerMigrator(new Migrator() {
@Override
public int from() {
return 1;
}
@Override
public int to() {
return 2;
}
@Override
public boolean supports(Class<?> configType) {
return configType == MainConfig.class;
}
@Override
public void migrate(MutableDocument doc) {
Object value = doc.get("old-name");
doc.set("new-name", value);
doc.remove("old-name");
}
});
MainConfig config = configService.bind(MainConfig.class);
迁移链中每一步的 from() 必须等于上一步结束后的版本,to() 必须向目标版本推进。例如可以注册 1 -> 2、2 -> 3,也可以用一个迁移器完成 1 -> 3。同一配置类型和起始版本只能存在一个适用迁移器。需要一次注册多个迁移器时,可以使用 registerMigrators(...)。
讨论