clazz) {
this.file = new File(distPath);
this.clazz = clazz;
// 如果配置文件不存在则复制默认配置
if (!file.exists()) {
try {
IO.resourceToDisk(getClass(), srcPath, distPath);
} catch (Exception e) {
throw new TimiException(TimiCode.ERROR, "load default config error", e);
}
}
// 配置 SnakeYAML
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowRecursiveKeys(false); // 禁止递归键
loaderOptions.setMaxAliasesForCollections(50); // 限制别名数量
loaderOptions.setCodePointLimit((int) (IOSize.MB * 3)); // 限制 3 MB
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setIndent(4);
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setIndicatorIndent(4);
dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
dumperOptions.setIndentWithIndicator(true);
// 使用自定义 Representer,跳过 null 值以减少配置文件体积
Representer representer = new ConfigRepresenter(dumperOptions);
// 根据类型创建 Constructor
Constructor constructor;
if (clazz != null && !clazz.equals(Object.class)) {
constructor = new Constructor(clazz, loaderOptions);
} else {
constructor = new Constructor(loaderOptions);
}
this.yaml = new Yaml(constructor, representer, dumperOptions);
this.rawYaml = new Yaml(new Constructor(loaderOptions));
}
/** 加载配置 */
@SuppressWarnings("unchecked")
public T load() {
try {
String content = IO.toString(file);
String normalized = content.replaceAll("\t", " ");
if (converters.isEmpty()) {
// SnakeYAML 自动转换 - 使用指定类型构造
value = yaml.load(normalized);
return value;
}
Object raw = rawYaml.load(normalized);
if (raw == null) {
value = null;
return null;
}
if (clazz == null || clazz.equals(Object.class)) {
value = (T) raw;
return value;
}
if (raw instanceof Map, ?> rawMap) {
value = mapToBean(rawMap, clazz);
return value;
}
value = (T) raw;
return value;
} catch (Exception e) {
throw new TimiException(TimiCode.ERROR, "load config error: " + file.getAbsolutePath(), e);
}
}
/** 保存配置 */
public void dump() {
try {
// 应用序列化转换器
Object toSerialize = applyConvertersOnDump(value);
// SnakeYAML 序列化
String yamlContent = yaml.dump(toSerialize);
// 空格转制表符
String toWrite = yamlContent.replaceAll("( ){4}", " ");
IO.toFile(file, toWrite);
} catch (Exception e) {
throw new TimiException(TimiCode.ERROR, "dump config error: " + file.getAbsolutePath(), e);
}
}
/**
* 添加自定义类型转换器
*
* 用于 YAML 不支持的类型序列化和反序列化,如 JavaFX Property 等
*
* @param type 字段类型
* @param converter 转换器
*/
public void addConverter(Class> type, BaseConverter, ?> converter) {
converters.put(type, converter);
}
private K mapToBean(Map, ?> map, Class targetClass) {
try {
K instance = targetClass.getDeclaredConstructor().newInstance();
applyMapToObject(map, instance);
return instance;
} catch (Exception e) {
throw new TimiException(TimiCode.ERROR, "map to bean error: " + targetClass.getName(), e);
}
}
private void applyMapToObject(Map, ?> map, Object target) throws IllegalAccessException {
Class> currentClass = target.getClass();
while (currentClass != null && currentClass != Object.class) {
for (Field field : currentClass.getDeclaredFields()) {
String fieldName = field.getName();
if (!map.containsKey(fieldName)) {
continue;
}
Object rawValue = map.get(fieldName);
Object converted = convertFieldValue(field, rawValue);
field.setAccessible(true);
field.set(target, converted);
}
currentClass = currentClass.getSuperclass();
}
}
private Object convertFieldValue(Field field, Object rawValue) {
BaseConverter, ?> converter = converters.get(field.getType());
if (converter != null) {
return converter.deserialize0(field, rawValue);
}
if (rawValue == null) {
return null;
}
Class> fieldType = field.getType();
if (fieldType.isEnum()) {
return toEnumValue(fieldType, rawValue.toString());
}
if (rawValue instanceof Map, ?> rawMap && shouldMapToBean(fieldType)) {
return mapToBean(rawMap, fieldType);
}
return convertScalarValue(fieldType, rawValue);
}
private boolean shouldMapToBean(Class> fieldType) {
if (fieldType.isPrimitive() || fieldType.isEnum()) {
return false;
}
if (fieldType.getName().startsWith("java.")) {
return false;
}
return !fieldType.isInterface();
}
private Object convertScalarValue(Class> fieldType, Object rawValue) {
if (fieldType.isInstance(rawValue)) {
return rawValue;
}
String text = rawValue.toString();
if (fieldType == String.class) {
return text;
}
if (fieldType == int.class || fieldType == Integer.class) {
return rawValue instanceof Number ? ((Number) rawValue).intValue() : Integer.parseInt(text);
}
if (fieldType == long.class || fieldType == Long.class) {
return rawValue instanceof Number ? ((Number) rawValue).longValue() : Long.parseLong(text);
}
if (fieldType == double.class || fieldType == Double.class) {
return rawValue instanceof Number ? ((Number) rawValue).doubleValue() : Double.parseDouble(text);
}
if (fieldType == float.class || fieldType == Float.class) {
return rawValue instanceof Number ? ((Number) rawValue).floatValue() : Float.parseFloat(text);
}
if (fieldType == short.class || fieldType == Short.class) {
return rawValue instanceof Number ? ((Number) rawValue).shortValue() : Short.parseShort(text);
}
if (fieldType == byte.class || fieldType == Byte.class) {
return rawValue instanceof Number ? ((Number) rawValue).byteValue() : Byte.parseByte(text);
}
if (fieldType == boolean.class || fieldType == Boolean.class) {
return rawValue instanceof Boolean ? rawValue : Boolean.parseBoolean(text);
}
if (fieldType == char.class || fieldType == Character.class) {
return text.isEmpty() ? null : text.charAt(0);
}
return rawValue;
}
@SuppressWarnings("unchecked")
private > E toEnumValue(Class> enumType, String value) {
Class enumClass = (Class) enumType;
for (E constant : enumClass.getEnumConstants()) {
if (constant.name().equalsIgnoreCase(value)) {
return constant;
}
}
return null;
}
/** 序列化时应用转换器 */
@SuppressWarnings("unchecked")
private K applyConvertersOnDump(K obj) {
if (obj == null || converters.isEmpty()) {
return obj;
}
try {
// 创建副本
K copy = (K) obj.getClass().getDeclaredConstructor().newInstance();
Class> currentClass = obj.getClass();
while (currentClass != null && currentClass != Object.class) {
for (Field field : currentClass.getDeclaredFields()) {
field.setAccessible(true);
Object fieldValue = field.get(obj);
BaseConverter, ?> converter = converters.get(field.getType());
if (converter != null) {
// 应用转换器
Object yamlValue = converter.serialize0(field, fieldValue);
field.set(copy, yamlValue);
} else {
// 直接复制
field.set(copy, fieldValue);
}
}
currentClass = currentClass.getSuperclass();
}
return copy;
} catch (Exception e) {
throw new TimiException(TimiCode.ERROR, "converter error on dump", e);
}
}
}