diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java index 647a46cad..da197655f 100644 --- a/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java +++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/Setting.java @@ -80,27 +80,18 @@ public class Setting extends AbsSetting implements Map { /** * 附带分组的键值对存储 */ - 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 { * 空构造 */ public Setting() { - this.charset = DEFAULT_CHARSET; + groupedMap = new GroupedMap(); } /** @@ -139,8 +130,7 @@ public class Setting extends AbsSetting implements Map { * @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 { * @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 { * @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 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 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 { return props; } - // --------------------------------------------------------------------------------- Functions - /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化不会保留之前的分组,注意如果配置文件在jar内部或者在exe中,此方法会报错。 @@ -374,10 +358,8 @@ public class Setting extends AbsSetting implements Map { * @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 { // 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 { * @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 { */ @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 { 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 { 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 { diff --git a/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java b/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java index f5d0107c8..1dba1b2d0 100644 --- a/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java +++ b/hutool-setting/src/main/java/org/dromara/hutool/setting/SettingLoader.java @@ -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 /** * 设置变量的正则
@@ -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 /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化会不会保留之前的分组 * + * @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)); } /** * 持久化当前设置,会覆盖掉之前的设置
* 持久化会不会保留之前的分组 * + * @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> groupEntry : this.groupedMap.entrySet()) { + synchronized private void store(final GroupedMap groupedMap, final PrintWriter writer) { + for (final Entry> groupEntry : groupedMap.entrySet()) { writer.println(StrUtil.format("{}{}{}", CharUtil.BRACKET_START, groupEntry.getKey(), CharUtil.BRACKET_END)); for (final Entry 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 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 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)); } } // 系统参数和环境变量中查找