This commit is contained in:
Looly 2024-11-06 16:22:08 +08:00
parent 9c6225dafa
commit 2d068f93fe
2 changed files with 174 additions and 218 deletions

View File

@ -80,27 +80,18 @@ public class Setting extends AbsSetting implements Map<String, String> {
/** /**
* 附带分组的键值对存储 * 附带分组的键值对存储
*/ */
private final GroupedMap groupedMap = new GroupedMap(); private GroupedMap groupedMap;
/**
* 本设置对象的字符集
*/
protected Charset charset;
/**
* 是否使用变量
*/
protected boolean isUseVariable;
/**
* 设定文件的资源
*/
protected Resource resource;
/** /**
* 当获取key对应值为{@code null}时是否打印debug日志提示用户默认{@code false} * 当获取key对应值为{@code null}时是否打印debug日志提示用户默认{@code false}
*/ */
private boolean logIfNull; private boolean logIfNull;
private SettingLoader settingLoader; /**
* 设定文件的资源
*/
protected Resource resource;
private SettingLoader loader;
private WatchMonitor watchMonitor; private WatchMonitor watchMonitor;
// region ----- Constructor // region ----- Constructor
@ -109,7 +100,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* 空构造 * 空构造
*/ */
public Setting() { public Setting() {
this.charset = DEFAULT_CHARSET; groupedMap = new GroupedMap();
} }
/** /**
@ -139,8 +130,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @param isUseVariable 是否使用变量 * @param isUseVariable 是否使用变量
*/ */
public Setting(final String path, final Charset charset, final boolean isUseVariable) { public Setting(final String path, final Charset charset, final boolean isUseVariable) {
Assert.notBlank(path, "Blank setting path !"); this(ResourceUtil.getResource(Assert.notBlank(path)), charset, isUseVariable);
this.init(ResourceUtil.getResource(path), charset, isUseVariable);
} }
/** /**
@ -151,8 +141,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @param isUseVariable 是否使用变量 * @param isUseVariable 是否使用变量
*/ */
public Setting(final File configFile, final Charset charset, final boolean isUseVariable) { public Setting(final File configFile, final Charset charset, final boolean isUseVariable) {
Assert.notNull(configFile, "Null setting file define!"); this(ResourceUtil.getResource(Assert.notNull(configFile)), charset, isUseVariable);
this.init(ResourceUtil.getResource(configFile), charset, isUseVariable);
} }
/** /**
@ -164,56 +153,49 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @since 5.4.4 * @since 5.4.4
*/ */
public Setting(final Resource resource, final Charset charset, final boolean isUseVariable) { public Setting(final Resource resource, final Charset charset, final boolean isUseVariable) {
this.init(resource, charset, isUseVariable); this(resource, new SettingLoader(charset, isUseVariable));
}
/**
* 构造
*
* @param resource Setting的Resource
* @param loader 自定义配置文件加载器
*/
public Setting(final Resource resource, SettingLoader loader) {
this.resource = resource;
if (null == loader) {
loader = new SettingLoader(DEFAULT_CHARSET, false);
}
this.loader = loader;
this.groupedMap = loader.load(resource);
} }
// endregion // endregion
/**
* 初始化设定文件
*
* @param resource {@link Resource}
* @param charset 字符集
* @param isUseVariable 是否使用变量
* @return 成功初始化与否
*/
public boolean init(final Resource resource, final Charset charset, final boolean isUseVariable) {
Assert.notNull(resource, "Setting resource must be not null!");
this.resource = resource;
this.charset = charset;
this.isUseVariable = isUseVariable;
return load();
}
/** /**
* 重新加载配置文件 * 重新加载配置文件
* *
* @return 是否加载成功 * @return this
*/ */
synchronized public boolean load() { synchronized public Setting load() {
if (null == this.settingLoader) { Assert.notNull(this.loader, "SettingLoader must be not null!");
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable); this.groupedMap = loader.load(this.resource);
} return this;
return settingLoader.load(this.resource);
} }
/** /**
* 在配置文件变更时自动加载 * 在配置文件变更时自动加载
*
* @param autoReload 是否自动加载
*/ */
public void autoLoad(final boolean autoReload) { public void autoLoad() {
autoLoad(autoReload, null); autoLoad(null);
} }
/** /**
* 在配置文件变更时自动加载 * 在配置文件变更时自动加载
* *
* @param callback 加载完成回调 * @param callback 加载完成回调
* @param autoReload 是否自动加载
*/ */
public void autoLoad(final boolean autoReload, final Consumer<Boolean> callback) { public void autoLoad(final Consumer<Setting> callback) {
if (autoReload) {
Assert.notNull(this.resource, "Setting resource must be not null !"); Assert.notNull(this.resource, "Setting resource must be not null !");
// 先关闭之前的监听 // 先关闭之前的监听
IoUtil.closeQuietly(this.watchMonitor); IoUtil.closeQuietly(this.watchMonitor);
@ -222,20 +204,24 @@ public class Setting extends AbsSetting implements Map<String, String> {
@Override @Override
public void onModify(final WatchEvent<?> event, final WatchKey key) { public void onModify(final WatchEvent<?> event, final WatchKey key) {
final boolean success = load(); load();
// 如果有回调加载完毕则执行回调 // 如果有回调加载完毕则执行回调
if (callback != null) { if (callback != null) {
callback.accept(success); callback.accept(Setting.this);
} }
} }
}); });
this.watchMonitor.start(); this.watchMonitor.start();
LogUtil.debug("Auto load for [{}] listenning...", this.resource.getUrl()); LogUtil.debug("Auto load for [{}] listenning...", this.resource.getUrl());
} else { }
/**
* 停止自动加载
*/
public void stopAutoLoad() {
IoUtil.closeQuietly(this.watchMonitor); IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = null; this.watchMonitor = null;
} }
}
/** /**
* 获得设定文件的URL * 获得设定文件的URL
@ -342,8 +328,6 @@ public class Setting extends AbsSetting implements Map<String, String> {
return props; return props;
} }
// --------------------------------------------------------------------------------- Functions
/** /**
* 持久化当前设置会覆盖掉之前的设置<br> * 持久化当前设置会覆盖掉之前的设置<br>
* 持久化不会保留之前的分组注意如果配置文件在jar内部或者在exe中此方法会报错 * 持久化不会保留之前的分组注意如果配置文件在jar内部或者在exe中此方法会报错
@ -374,10 +358,8 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @since 5.4.3 * @since 5.4.3
*/ */
public void store(final File file) { public void store(final File file) {
if (null == this.settingLoader) { Assert.notNull(this.loader, "SettingLoader must be not null!");
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable); this.loader.store(this.groupedMap, file);
}
settingLoader.store(file);
} }
/** /**
@ -429,27 +411,16 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @return this * @return this
*/ */
public Setting setVarRegex(final String regex) { public Setting setVarRegex(final String regex) {
if (null == this.settingLoader) { if (null == this.loader) {
throw new NullPointerException("SettingLoader is null !"); throw new NullPointerException("SettingLoader is null !");
} }
this.settingLoader.setVarRegex(regex); this.loader.setVarRegex(regex);
return this;
}
/**
* 自定义字符编码
*
* @param charset 字符编码
* @return this
* @since 4.6.2
*/
public Setting setCharset(final Charset charset) {
this.charset = charset;
return this; return this;
} }
/** /**
* 设置当获取key对应值为{@code null}时是否打印debug日志提示用户 * 设置当获取key对应值为{@code null}时是否打印debug日志提示用户
*
* @param logIfNull 当获取key对应值为{@code null}时是否打印debug日志提示用户 * @param logIfNull 当获取key对应值为{@code null}时是否打印debug日志提示用户
* @return this * @return this
*/ */
@ -742,9 +713,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((charset == null) ? 0 : charset.hashCode());
result = prime * result + groupedMap.hashCode(); result = prime * result + groupedMap.hashCode();
result = prime * result + (isUseVariable ? 1231 : 1237);
result = prime * result + ((this.resource == null) ? 0 : this.resource.hashCode()); result = prime * result + ((this.resource == null) ? 0 : this.resource.hashCode());
return result; return result;
} }
@ -761,19 +730,9 @@ public class Setting extends AbsSetting implements Map<String, String> {
return false; return false;
} }
final Setting other = (Setting) obj; final Setting other = (Setting) obj;
if (charset == null) {
if (other.charset != null) {
return false;
}
} else if (!charset.equals(other.charset)) {
return false;
}
if (!groupedMap.equals(other.groupedMap)) { if (!groupedMap.equals(other.groupedMap)) {
return false; return false;
} }
if (isUseVariable != other.isUseVariable) {
return false;
}
if (this.resource == null) { if (this.resource == null) {
return other.resource == null; return other.resource == null;
} else { } else {

View File

@ -25,7 +25,6 @@ import org.dromara.hutool.core.regex.ReUtil;
import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil; import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.util.SystemUtil; import org.dromara.hutool.core.util.SystemUtil;
import org.dromara.hutool.log.Log; import org.dromara.hutool.log.Log;
@ -62,11 +61,6 @@ public class SettingLoader {
* 是否使用变量 * 是否使用变量
*/ */
private final boolean isUseVariable; private final boolean isUseVariable;
/**
* GroupedMap
*/
private final GroupedMap groupedMap;
/** /**
* 赋值分隔符用于分隔键值对 * 赋值分隔符用于分隔键值对
*/ */
@ -83,103 +77,15 @@ public class SettingLoader {
/** /**
* 构造 * 构造
* *
* @param groupedMap GroupedMap
*/
public SettingLoader(final GroupedMap groupedMap) {
this(groupedMap, CharsetUtil.UTF_8, false);
}
/**
* 构造
*
* @param groupedMap GroupedMap
* @param charset 编码 * @param charset 编码
* @param isUseVariable 是否使用变量 * @param isUseVariable 是否使用变量
*/ */
public SettingLoader(final GroupedMap groupedMap, final Charset charset, final boolean isUseVariable) { public SettingLoader(final Charset charset, final boolean isUseVariable) {
this.groupedMap = groupedMap;
this.charset = charset; this.charset = charset;
this.isUseVariable = isUseVariable; this.isUseVariable = isUseVariable;
} }
/** // region ----- setXXX
* 加载设置文件
*
* @param resource 配置文件URL
* @return 加载是否成功
*/
public boolean load(final Resource resource) {
if (resource == null) {
throw new NullPointerException("Null setting url define!");
}
log.debug("Load setting file [{}]", resource);
InputStream settingStream = null;
try {
settingStream = resource.getStream();
load(settingStream);
} catch (final Exception e) {
log.error(e, "Load setting error!");
return false;
} finally {
IoUtil.closeQuietly(settingStream);
}
return true;
}
/**
* 加载设置文件 此方法不会关闭流对象
*
* @param settingStream 文件流
* @throws IOException IO异常
*/
synchronized public void load(final InputStream settingStream) throws IOException {
this.groupedMap.clear();
LineReader reader = null;
try {
reader = new LineReader(settingStream, this.charset);
// 分组
String group = null;
String line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
line = StrUtil.trim(line);
// 跳过注释行和空行
if (StrUtil.isBlank(line) || StrUtil.startWith(line, COMMENT_FLAG_PRE)) {
continue;
}
// 记录分组名
if (StrUtil.isWrap(line, CharUtil.BRACKET_START, CharUtil.BRACKET_END)) {
group = StrUtil.trim(line.substring(1, line.length() - 1));
continue;
}
final String[] keyValue = SplitUtil.split(line, String.valueOf(this.assignFlag), 2, true, false)
.toArray(new String[0]);
// 跳过不符合键值规范的行
if (keyValue.length < 2) {
continue;
}
String value = keyValue[1];
if (null != this.valueEditor) {
value = this.valueEditor.apply(value);
}
// 替换值中的所有变量变量变量必须是此行之前定义的变量否则无法找到
if (this.isUseVariable) {
value = replaceVar(group, value);
}
this.groupedMap.put(group, StrUtil.trim(keyValue[0]), value);
}
} finally {
IoUtil.closeQuietly(reader);
}
}
/** /**
* 设置变量的正则<br> * 设置变量的正则<br>
@ -218,31 +124,120 @@ public class SettingLoader {
this.valueEditor = valueEditor; this.valueEditor = valueEditor;
return this; return this;
} }
// endregion
// region ----- load
/**
* 加载设置文件
*
* @param resource 配置文件URL
* @return 加载是否成功
*/
public GroupedMap load(final Resource resource) {
if (resource == null) {
throw new NullPointerException("Null setting url define!");
}
log.debug("Load setting file [{}]", resource);
InputStream settingStream = null;
try {
settingStream = resource.getStream();
return load(settingStream);
} catch (final Exception e) {
log.error(e, "Load setting error!");
// 加载错误跳过返回空的map
return new GroupedMap();
} finally {
IoUtil.closeQuietly(settingStream);
}
}
/**
* 加载设置文件 此方法不会关闭流对象
*
* @param settingStream 文件流
* @return {@link GroupedMap}
* @throws IOException IO异常
*/
synchronized public GroupedMap load(final InputStream settingStream) throws IOException {
final GroupedMap groupedMap = new GroupedMap();
LineReader reader = null;
try {
reader = new LineReader(settingStream, this.charset);
// 分组
String group = null;
String line;
while (true) {
line = reader.readLine();
if (line == null) {
break;
}
line = StrUtil.trim(line);
// 跳过注释行和空行
if (StrUtil.isBlank(line) || StrUtil.startWith(line, COMMENT_FLAG_PRE)) {
continue;
}
// 记录分组名
if (StrUtil.isWrap(line, CharUtil.BRACKET_START, CharUtil.BRACKET_END)) {
group = StrUtil.trim(line.substring(1, line.length() - 1));
continue;
}
final String[] keyValue = SplitUtil.split(line, String.valueOf(this.assignFlag), 2, true, false)
.toArray(new String[0]);
// 跳过不符合键值规范的行
if (keyValue.length < 2) {
continue;
}
String value = keyValue[1];
if (null != this.valueEditor) {
value = this.valueEditor.apply(value);
}
// 替换值中的所有变量变量变量必须是此行之前定义的变量否则无法找到
if (this.isUseVariable) {
value = replaceVar(groupedMap, group, value);
}
groupedMap.put(group, StrUtil.trim(keyValue[0]), value);
}
} finally {
IoUtil.closeQuietly(reader);
}
return groupedMap;
}
// endregion
// region ----- store
/** /**
* 持久化当前设置会覆盖掉之前的设置<br> * 持久化当前设置会覆盖掉之前的设置<br>
* 持久化会不会保留之前的分组 * 持久化会不会保留之前的分组
* *
* @param groupedMap 分组map
* @param absolutePath 设置文件的绝对路径 * @param absolutePath 设置文件的绝对路径
*/ */
public void store(final String absolutePath) { public void store(final GroupedMap groupedMap, final String absolutePath) {
store(FileUtil.touch(absolutePath)); store(groupedMap, FileUtil.touch(absolutePath));
} }
/** /**
* 持久化当前设置会覆盖掉之前的设置<br> * 持久化当前设置会覆盖掉之前的设置<br>
* 持久化会不会保留之前的分组 * 持久化会不会保留之前的分组
* *
* @param groupedMap 分组map
* @param file 设置文件 * @param file 设置文件
* @since 5.4.3
*/ */
public void store(final File file) { public void store(final GroupedMap groupedMap, final File file) {
Assert.notNull(file, "File to store must be not null !"); Assert.notNull(file, "File to store must be not null !");
log.debug("Store Setting to [{}]...", file.getAbsolutePath()); log.debug("Store Setting to [{}]...", file.getAbsolutePath());
PrintWriter writer = null; PrintWriter writer = null;
try { try {
writer = FileUtil.getPrintWriter(file, charset, false); writer = FileUtil.getPrintWriter(file, charset, false);
store(writer); store(groupedMap, writer);
} finally { } finally {
IoUtil.closeQuietly(writer); IoUtil.closeQuietly(writer);
} }
@ -251,16 +246,18 @@ public class SettingLoader {
/** /**
* 存储到Writer * 存储到Writer
* *
* @param groupedMap 分组Map
* @param writer Writer * @param writer Writer
*/ */
synchronized private void store(final PrintWriter writer) { synchronized private void store(final GroupedMap groupedMap, final PrintWriter writer) {
for (final Entry<String, LinkedHashMap<String, String>> groupEntry : this.groupedMap.entrySet()) { for (final Entry<String, LinkedHashMap<String, String>> groupEntry : groupedMap.entrySet()) {
writer.println(StrUtil.format("{}{}{}", CharUtil.BRACKET_START, groupEntry.getKey(), CharUtil.BRACKET_END)); writer.println(StrUtil.format("{}{}{}", CharUtil.BRACKET_START, groupEntry.getKey(), CharUtil.BRACKET_END));
for (final Entry<String, String> entry : groupEntry.getValue().entrySet()) { for (final Entry<String, String> entry : groupEntry.getValue().entrySet()) {
writer.println(StrUtil.format("{} {} {}", entry.getKey(), this.assignFlag, entry.getValue())); writer.println(StrUtil.format("{} {} {}", entry.getKey(), this.assignFlag, entry.getValue()));
} }
} }
} }
// endregion
// ----------------------------------------------------------------------------------- Private method start // ----------------------------------------------------------------------------------- Private method start
@ -271,7 +268,7 @@ public class SettingLoader {
* @param value * @param value
* @return 替换后的字符串 * @return 替换后的字符串
*/ */
private String replaceVar(final String group, String value) { private String replaceVar(final GroupedMap groupedMap, final String group, String value) {
// 找到所有变量标识 // 找到所有变量标识
final Set<String> vars = ReUtil.findAll(varRegex, value, 0, new HashSet<>()); final Set<String> vars = ReUtil.findAll(varRegex, value, 0, new HashSet<>());
String key; String key;
@ -279,12 +276,12 @@ public class SettingLoader {
key = ReUtil.get(varRegex, var, 1); key = ReUtil.get(varRegex, var, 1);
if (StrUtil.isNotBlank(key)) { if (StrUtil.isNotBlank(key)) {
// 本分组中查找变量名对应的值 // 本分组中查找变量名对应的值
String varValue = this.groupedMap.get(group, key); String varValue = groupedMap.get(group, key);
// 跨分组查找 // 跨分组查找
if (null == varValue) { if (null == varValue) {
final List<String> groupAndKey = SplitUtil.split(key, StrUtil.DOT, 2, true, false); final List<String> groupAndKey = SplitUtil.split(key, StrUtil.DOT, 2, true, false);
if (groupAndKey.size() > 1) { if (groupAndKey.size() > 1) {
varValue = this.groupedMap.get(groupAndKey.get(0), groupAndKey.get(1)); varValue = groupedMap.get(groupAndKey.get(0), groupAndKey.get(1));
} }
} }
// 系统参数和环境变量中查找 // 系统参数和环境变量中查找