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();
/**
* 本设置对象的字符集
*/
protected Charset charset;
/**
* 是否使用变量
*/
protected boolean isUseVariable;
/**
* 设定文件的资源
*/
protected Resource resource;
private GroupedMap groupedMap;
/**
* 当获取key对应值为{@code null}时是否打印debug日志提示用户默认{@code false}
*/
private boolean logIfNull;
private SettingLoader settingLoader;
/**
* 设定文件的资源
*/
protected Resource resource;
private SettingLoader loader;
private WatchMonitor watchMonitor;
// region ----- Constructor
@ -109,7 +100,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* 空构造
*/
public Setting() {
this.charset = DEFAULT_CHARSET;
groupedMap = new GroupedMap();
}
/**
@ -139,8 +130,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @param isUseVariable 是否使用变量
*/
public Setting(final String path, final Charset charset, final boolean isUseVariable) {
Assert.notBlank(path, "Blank setting path !");
this.init(ResourceUtil.getResource(path), charset, isUseVariable);
this(ResourceUtil.getResource(Assert.notBlank(path)), charset, isUseVariable);
}
/**
@ -151,8 +141,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @param isUseVariable 是否使用变量
*/
public Setting(final File configFile, final Charset charset, final boolean isUseVariable) {
Assert.notNull(configFile, "Null setting file define!");
this.init(ResourceUtil.getResource(configFile), charset, isUseVariable);
this(ResourceUtil.getResource(Assert.notNull(configFile)), charset, isUseVariable);
}
/**
@ -164,77 +153,74 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @since 5.4.4
*/
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
/**
* 初始化设定文件
*
* @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() {
if (null == this.settingLoader) {
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable);
}
return settingLoader.load(this.resource);
synchronized public Setting load() {
Assert.notNull(this.loader, "SettingLoader must be not null!");
this.groupedMap = loader.load(this.resource);
return this;
}
/**
* 在配置文件变更时自动加载
*/
public void autoLoad() {
autoLoad(null);
}
/**
* 在配置文件变更时自动加载
*
* @param autoReload 是否自动加载
* @param callback 加载完成回调
*/
public void autoLoad(final boolean autoReload) {
autoLoad(autoReload, null);
}
public void autoLoad(final Consumer<Setting> callback) {
Assert.notNull(this.resource, "Setting resource must be not null !");
// 先关闭之前的监听
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = WatchUtil.ofModify(resource.getUrl(), new SimpleWatcher() {
private static final long serialVersionUID = 5190107321461226190L;
/**
* 在配置文件变更时自动加载
*
* @param callback 加载完成回调
* @param autoReload 是否自动加载
*/
public void autoLoad(final boolean autoReload, final Consumer<Boolean> callback) {
if (autoReload) {
Assert.notNull(this.resource, "Setting resource must be not null !");
// 先关闭之前的监听
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = WatchUtil.ofModify(resource.getUrl(), new SimpleWatcher() {
private static final long serialVersionUID = 5190107321461226190L;
@Override
public void onModify(final WatchEvent<?> event, final WatchKey key) {
final boolean success = load();
// 如果有回调加载完毕则执行回调
if (callback != null) {
callback.accept(success);
}
@Override
public void onModify(final WatchEvent<?> event, final WatchKey key) {
load();
// 如果有回调加载完毕则执行回调
if (callback != null) {
callback.accept(Setting.this);
}
});
this.watchMonitor.start();
LogUtil.debug("Auto load for [{}] listenning...", this.resource.getUrl());
} else {
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = null;
}
}
});
this.watchMonitor.start();
LogUtil.debug("Auto load for [{}] listenning...", this.resource.getUrl());
}
/**
* 停止自动加载
*/
public void stopAutoLoad() {
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = null;
}
/**
@ -342,8 +328,6 @@ public class Setting extends AbsSetting implements Map<String, String> {
return props;
}
// --------------------------------------------------------------------------------- Functions
/**
* 持久化当前设置会覆盖掉之前的设置<br>
* 持久化不会保留之前的分组注意如果配置文件在jar内部或者在exe中此方法会报错
@ -374,10 +358,8 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @since 5.4.3
*/
public void store(final File file) {
if (null == this.settingLoader) {
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable);
}
settingLoader.store(file);
Assert.notNull(this.loader, "SettingLoader must be not null!");
this.loader.store(this.groupedMap, file);
}
/**
@ -394,7 +376,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
// issue#I9B98C忽略null的键值对
final String key = entry.getKey();
final String value = entry.getValue();
if(null != key && null != value){
if (null != key && null != value) {
props.setProperty(StrUtil.isEmpty(group) ? key : group + CharUtil.DOT + key, value);
}
}
@ -429,31 +411,20 @@ public class Setting extends AbsSetting implements Map<String, String> {
* @return this
*/
public Setting setVarRegex(final String regex) {
if (null == this.settingLoader) {
if (null == this.loader) {
throw new NullPointerException("SettingLoader is null !");
}
this.settingLoader.setVarRegex(regex);
return this;
}
/**
* 自定义字符编码
*
* @param charset 字符编码
* @return this
* @since 4.6.2
*/
public Setting setCharset(final Charset charset) {
this.charset = charset;
this.loader.setVarRegex(regex);
return this;
}
/**
* 设置当获取key对应值为{@code null}时是否打印debug日志提示用户
*
* @param logIfNull 当获取key对应值为{@code null}时是否打印debug日志提示用户
* @return this
*/
public Setting setLogIfNull(final boolean logIfNull){
public Setting setLogIfNull(final boolean logIfNull) {
this.logIfNull = logIfNull;
return this;
}
@ -663,7 +634,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
*/
@Override
public String get(final Object key) {
return getStr((String)key);
return getStr((String) key);
}
/**
@ -742,9 +713,7 @@ public class Setting extends AbsSetting implements Map<String, String> {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((charset == null) ? 0 : charset.hashCode());
result = prime * result + groupedMap.hashCode();
result = prime * result + (isUseVariable ? 1231 : 1237);
result = prime * result + ((this.resource == null) ? 0 : this.resource.hashCode());
return result;
}
@ -761,19 +730,9 @@ public class Setting extends AbsSetting implements Map<String, String> {
return false;
}
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)) {
return false;
}
if (isUseVariable != other.isUseVariable) {
return false;
}
if (this.resource == null) {
return other.resource == null;
} 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.StrUtil;
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.log.Log;
@ -62,11 +61,6 @@ public class SettingLoader {
* 是否使用变量
*/
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 isUseVariable 是否使用变量
*/
public SettingLoader(final GroupedMap groupedMap, final Charset charset, final boolean isUseVariable) {
this.groupedMap = groupedMap;
public SettingLoader(final Charset charset, final boolean isUseVariable) {
this.charset = charset;
this.isUseVariable = isUseVariable;
}
/**
* 加载设置文件
*
* @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);
}
}
// region ----- setXXX
/**
* 设置变量的正则<br>
@ -218,31 +124,120 @@ public class SettingLoader {
this.valueEditor = valueEditor;
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>
* 持久化会不会保留之前的分组
*
* @param groupedMap 分组map
* @param absolutePath 设置文件的绝对路径
*/
public void store(final String absolutePath) {
store(FileUtil.touch(absolutePath));
public void store(final GroupedMap groupedMap, final String absolutePath) {
store(groupedMap, FileUtil.touch(absolutePath));
}
/**
* 持久化当前设置会覆盖掉之前的设置<br>
* 持久化会不会保留之前的分组
*
* @param groupedMap 分组map
* @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 !");
log.debug("Store Setting to [{}]...", file.getAbsolutePath());
PrintWriter writer = null;
try {
writer = FileUtil.getPrintWriter(file, charset, false);
store(writer);
store(groupedMap, writer);
} finally {
IoUtil.closeQuietly(writer);
}
@ -251,16 +246,18 @@ public class SettingLoader {
/**
* 存储到Writer
*
* @param writer Writer
* @param groupedMap 分组Map
* @param writer Writer
*/
synchronized private void store(final PrintWriter writer) {
for (final Entry<String, LinkedHashMap<String, String>> groupEntry : this.groupedMap.entrySet()) {
synchronized private void store(final GroupedMap groupedMap, final PrintWriter writer) {
for (final Entry<String, LinkedHashMap<String, String>> groupEntry : groupedMap.entrySet()) {
writer.println(StrUtil.format("{}{}{}", CharUtil.BRACKET_START, groupEntry.getKey(), CharUtil.BRACKET_END));
for (final Entry<String, String> entry : groupEntry.getValue().entrySet()) {
writer.println(StrUtil.format("{} {} {}", entry.getKey(), this.assignFlag, entry.getValue()));
}
}
}
// endregion
// ----------------------------------------------------------------------------------- Private method start
@ -271,7 +268,7 @@ public class SettingLoader {
* @param value
* @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<>());
String key;
@ -279,12 +276,12 @@ public class SettingLoader {
key = ReUtil.get(varRegex, var, 1);
if (StrUtil.isNotBlank(key)) {
// 本分组中查找变量名对应的值
String varValue = this.groupedMap.get(group, key);
String varValue = groupedMap.get(group, key);
// 跨分组查找
if (null == varValue) {
final List<String> groupAndKey = SplitUtil.split(key, StrUtil.DOT, 2, true, false);
if (groupAndKey.size() > 1) {
varValue = this.groupedMap.get(groupAndKey.get(0), groupAndKey.get(1));
varValue = groupedMap.get(groupAndKey.get(0), groupAndKey.get(1));
}
}
// 系统参数和环境变量中查找