mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
clean history
This commit is contained in:
293
hutool-setting/src/main/java/cn/hutool/setting/AbsSetting.java
Normal file
293
hutool-setting/src/main/java/cn/hutool/setting/AbsSetting.java
Normal file
@@ -0,0 +1,293 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.bean.copier.ValueProvider;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.getter.OptNullBasicTypeFromStringGetter;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import cn.hutool.log.LogFactory;
|
||||
|
||||
/**
|
||||
* Setting抽象类
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public abstract class AbsSetting extends OptNullBasicTypeFromStringGetter<String> implements Serializable{
|
||||
private static final long serialVersionUID = 6200156302595905863L;
|
||||
private final static Log log = LogFactory.get();
|
||||
|
||||
/** 数组类型值默认分隔符 */
|
||||
public final static String DEFAULT_DELIMITER = ",";
|
||||
/** 默认分组 */
|
||||
public final static String DEFAULT_GROUP = StrUtil.EMPTY;
|
||||
|
||||
@Override
|
||||
public String getStr(String key, String defaultValue) {
|
||||
return getStr(key, DEFAULT_GROUP, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得字符串类型值
|
||||
*
|
||||
* @param key KEY
|
||||
* @param group 分组
|
||||
* @param defaultValue 默认值
|
||||
* @return 值或默认值
|
||||
*/
|
||||
public String getStr(String key, String group, String defaultValue) {
|
||||
final String value = getByGroup(key, group);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定分组的键对应值
|
||||
*
|
||||
* @param key 键
|
||||
* @param group 分组
|
||||
* @return 值
|
||||
*/
|
||||
public abstract String getByGroup(String key, String group);
|
||||
|
||||
// --------------------------------------------------------------- Get
|
||||
/**
|
||||
* 带有日志提示的get,如果没有定义指定的KEY,则打印debug日志
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public String getWithLog(String key) {
|
||||
final String value = getStr(key);
|
||||
if (value == null) {
|
||||
log.debug("No key define for [{}]!", key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 带有日志提示的get,如果没有定义指定的KEY,则打印debug日志
|
||||
*
|
||||
* @param key 键
|
||||
* @param group 分组
|
||||
* @return 值
|
||||
*/
|
||||
public String getByGroupWithLog(String key, String group) {
|
||||
final String value = getByGroup(key, group);
|
||||
if (value == null) {
|
||||
log.debug("No key define for [{}] of group [{}] !", key, group);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get string array
|
||||
/**
|
||||
* 获得数组型
|
||||
*
|
||||
* @param key 属性名
|
||||
* @return 属性值
|
||||
*/
|
||||
public String[] getStrings(String key) {
|
||||
return getStrings(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数组型
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param defaultValue 默认的值
|
||||
* @return 属性值
|
||||
*/
|
||||
public String[] getStringsWithDefault(String key, String[] defaultValue) {
|
||||
String[] value = getStrings(key, null);
|
||||
if (null == value) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数组型
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public String[] getStrings(String key, String group) {
|
||||
return getStrings(key, group, DEFAULT_DELIMITER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得数组型
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @param delimiter 分隔符
|
||||
* @return 属性值
|
||||
*/
|
||||
public String[] getStrings(String key, String group, String delimiter) {
|
||||
final String value = getByGroup(key, group);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
return StrUtil.split(value, delimiter);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get int
|
||||
/**
|
||||
* 获取数字型型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public Integer getInt(String key, String group) {
|
||||
return getInt(key, group, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数字型型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @param defaultValue 默认值
|
||||
* @return 属性值
|
||||
*/
|
||||
public Integer getInt(String key, String group, Integer defaultValue) {
|
||||
return Convert.toInt(getByGroup(key, group), defaultValue);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get bool
|
||||
/**
|
||||
* 获取波尔型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public Boolean getBool(String key, String group) {
|
||||
return getBool(key, group, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取波尔型型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @param defaultValue 默认值
|
||||
* @return 属性值
|
||||
*/
|
||||
public Boolean getBool(String key, String group, Boolean defaultValue) {
|
||||
return Convert.toBool(getByGroup(key, group), defaultValue);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get long
|
||||
/**
|
||||
* 获取long类型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public Long getLong(String key, String group) {
|
||||
return getLong(key, group, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取long类型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @param defaultValue 默认值
|
||||
* @return 属性值
|
||||
*/
|
||||
public Long getLong(String key, String group, Long defaultValue) {
|
||||
return Convert.toLong(getByGroup(key, group), defaultValue);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get char
|
||||
/**
|
||||
* 获取char类型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public Character getChar(String key, String group) {
|
||||
final String value = getByGroup(key, group);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Get double
|
||||
/**
|
||||
* 获取double类型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @return 属性值
|
||||
*/
|
||||
public Double getDouble(String key, String group) {
|
||||
return getDouble(key, group, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取double类型属性值
|
||||
*
|
||||
* @param key 属性名
|
||||
* @param group 分组名
|
||||
* @param defaultValue 默认值
|
||||
* @return 属性值
|
||||
*/
|
||||
public Double getDouble(String key, String group, Double defaultValue) {
|
||||
return Convert.toDouble(getByGroup(key, group), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将setting中的键值关系映射到对象中,原理是调用对象对应的set方法<br>
|
||||
* 只支持基本类型的转换
|
||||
*
|
||||
* @param group 分组
|
||||
* @param bean Bean对象
|
||||
* @return Bean
|
||||
*/
|
||||
public Object toBean(final String group, Object bean) {
|
||||
return BeanUtil.fillBean(bean, new ValueProvider<String>(){
|
||||
|
||||
@Override
|
||||
public Object value(String key, Type valueType) {
|
||||
final String value = getByGroup(key, group);
|
||||
// if (null != value) {
|
||||
// log.debug("Parse setting to object field [{}={}]", key, value);
|
||||
// }
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
return null != getByGroup(key, group);
|
||||
}
|
||||
}, CopyOptions.create());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将setting中的键值关系映射到对象中,原理是调用对象对应的set方法<br>
|
||||
* 只支持基本类型的转换
|
||||
*
|
||||
* @param bean Bean
|
||||
* @return Bean
|
||||
*/
|
||||
public Object toBean(Object bean) {
|
||||
return toBean(null, bean);
|
||||
}
|
||||
}
|
321
hutool-setting/src/main/java/cn/hutool/setting/GroupedMap.java
Normal file
321
hutool-setting/src/main/java/cn/hutool/setting/GroupedMap.java
Normal file
@@ -0,0 +1,321 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 基于分组的Map<br>
|
||||
* 此对象方法线程安全
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.0.11
|
||||
*/
|
||||
public class GroupedMap extends LinkedHashMap<String, LinkedHashMap<String, String>> {
|
||||
private static final long serialVersionUID = -7777365130776081931L;
|
||||
|
||||
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
|
||||
private final ReadLock readLock = cacheLock.readLock();
|
||||
private final WriteLock writeLock = cacheLock.writeLock();
|
||||
private int size = -1;
|
||||
|
||||
/**
|
||||
* 获取分组对应的值,如果分组不存在或者值不存在则返回null
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 值,如果分组不存在或者值不存在则返回null
|
||||
*/
|
||||
public String get(String group, String key) {
|
||||
readLock.lock();
|
||||
try {
|
||||
LinkedHashMap<String, String> map = this.get(StrUtil.nullToEmpty(group));
|
||||
if (MapUtil.isNotEmpty(map)) {
|
||||
return map.get(key);
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<String, String> get(Object key) {
|
||||
readLock.lock();
|
||||
try {
|
||||
return super.get(key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 总的键值对数
|
||||
*
|
||||
* @return 总键值对数
|
||||
*/
|
||||
public int size() {
|
||||
writeLock.lock();
|
||||
try {
|
||||
if (this.size < 0) {
|
||||
this.size = 0;
|
||||
for (LinkedHashMap<String, String> value : this.values()) {
|
||||
this.size += value.size();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
return this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将键值对加入到对应分组中
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 此key之前存在的值,如果没有返回null
|
||||
*/
|
||||
public String put(String group, String key, String value) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
writeLock.lock();
|
||||
try {
|
||||
LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (null == valueMap) {
|
||||
valueMap = new LinkedHashMap<>();
|
||||
this.put(group, valueMap);
|
||||
}
|
||||
this.size = -1;
|
||||
return valueMap.put(key, value);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入多个键值对到某个分组下
|
||||
*
|
||||
* @param group 分组
|
||||
* @param m 键值对
|
||||
* @return this
|
||||
*/
|
||||
public GroupedMap putAll(String group, Map<? extends String, ? extends String> m) {
|
||||
for (Entry<? extends String, ? extends String> entry : m.entrySet()) {
|
||||
this.put(group, entry.getKey(), entry.getValue());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定分组中删除指定值
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 被删除的值,如果值不存在,返回null
|
||||
*/
|
||||
public String remove(String group, String key) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
writeLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.remove(key);
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 某个分组对应的键值对是否为空
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 是否为空
|
||||
*/
|
||||
public boolean isEmpty(String group) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.isEmpty();
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空,如果多个分组同时为空,也按照空处理
|
||||
*
|
||||
* @return 是否为空,如果多个分组同时为空,也按照空处理
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组中是否包含指定key
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 是否包含key
|
||||
*/
|
||||
public boolean containsKey(String group, String key) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.containsKey(key);
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组中是否包含指定值
|
||||
*
|
||||
* @param group 分组
|
||||
* @param value 值
|
||||
* @return 是否包含值
|
||||
*/
|
||||
public boolean containsValue(String group, String value) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.containsValue(value);
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定分组下的所有键值对
|
||||
*
|
||||
* @param group 分组
|
||||
* @return this
|
||||
*/
|
||||
public GroupedMap clear(String group) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
writeLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
valueMap.clear();
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
readLock.lock();
|
||||
try {
|
||||
return super.keySet();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组所有键的Set
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 键Set
|
||||
*/
|
||||
public Set<String> keySet(String group) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.keySet();
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组下所有值
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 值
|
||||
*/
|
||||
public Collection<String> values(String group) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.values();
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<java.util.Map.Entry<String, LinkedHashMap<String, String>>> entrySet() {
|
||||
readLock.lock();
|
||||
try {
|
||||
return super.entrySet();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组下所有键值对
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 键值对
|
||||
*/
|
||||
public Set<Entry<String, String>> entrySet(String group) {
|
||||
group = StrUtil.nullToEmpty(group).trim();
|
||||
readLock.lock();
|
||||
try {
|
||||
final LinkedHashMap<String, String> valueMap = this.get(group);
|
||||
if (MapUtil.isNotEmpty(valueMap)) {
|
||||
return valueMap.entrySet();
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
readLock.lock();
|
||||
try {
|
||||
return super.toString();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
315
hutool-setting/src/main/java/cn/hutool/setting/GroupedSet.java
Normal file
315
hutool-setting/src/main/java/cn/hutool/setting/GroupedSet.java
Normal file
@@ -0,0 +1,315 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
/**
|
||||
* 分组化的Set集合类<br>
|
||||
* 在配置文件中可以用中括号分隔不同的分组,每个分组会放在独立的Set中,用group区别<br>
|
||||
* 无分组的集合和`[]`分组集合会合并成员,重名的分组也会合并成员<br>
|
||||
* 分组配置文件如下:
|
||||
*
|
||||
* <pre>
|
||||
* [group1]
|
||||
* aaa
|
||||
* bbb
|
||||
* ccc
|
||||
*
|
||||
* [group2]
|
||||
* aaa
|
||||
* ccc
|
||||
* ddd
|
||||
* </pre>
|
||||
*
|
||||
* @author Looly
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public class GroupedSet extends HashMap<String, LinkedHashSet<String>> {
|
||||
private static final long serialVersionUID = -8430706353275835496L;
|
||||
// private final static Log log = StaticLog.get();
|
||||
|
||||
/** 注释符号(当有此符号在行首,表示此行为注释) */
|
||||
private final static String COMMENT_FLAG_PRE = "#";
|
||||
/** 分组行识别的环绕标记 */
|
||||
private final static char[] GROUP_SURROUND = { '[', ']' };
|
||||
|
||||
/** 本设置对象的字符集 */
|
||||
private Charset charset;
|
||||
/** 设定文件的URL */
|
||||
private URL groupedSetUrl;
|
||||
|
||||
/**
|
||||
* 基本构造<br>
|
||||
* 需自定义初始化配置文件
|
||||
*
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public GroupedSet(Charset charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用相对于Class文件根目录的相对路径
|
||||
*
|
||||
* @param pathBaseClassLoader 相对路径(相对于当前项目的classes路径)
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public GroupedSet(String pathBaseClassLoader, Charset charset) {
|
||||
if (null == pathBaseClassLoader) {
|
||||
pathBaseClassLoader = StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final URL url = URLUtil.getURL(pathBaseClassLoader);
|
||||
if (url == null) {
|
||||
throw new RuntimeException(StrUtil.format("Can not find GroupSet file: [{}]", pathBaseClassLoader));
|
||||
}
|
||||
this.init(url, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param configFile 配置文件对象
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public GroupedSet(File configFile, Charset charset) {
|
||||
if (configFile == null) {
|
||||
throw new RuntimeException("Null GroupSet file!");
|
||||
}
|
||||
final URL url = URLUtil.getURL(configFile);
|
||||
if (url == null) {
|
||||
throw new RuntimeException(StrUtil.format("Can not find GroupSet file: [{}]", configFile.getAbsolutePath()));
|
||||
}
|
||||
this.init(url, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,相对于classes读取文件
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @param clazz 基准类
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public GroupedSet(String path, Class<?> clazz, Charset charset) {
|
||||
final URL url = URLUtil.getURL(path, clazz);
|
||||
if (url == null) {
|
||||
throw new RuntimeException(StrUtil.format("Can not find GroupSet file: [{}]", path));
|
||||
}
|
||||
this.init(url, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param url 设定文件的URL
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public GroupedSet(URL url, Charset charset) {
|
||||
if (url == null) {
|
||||
throw new RuntimeException("Null url define!");
|
||||
}
|
||||
this.init(url, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param pathBaseClassLoader 相对路径(相对于当前项目的classes路径)
|
||||
*/
|
||||
public GroupedSet(String pathBaseClassLoader) {
|
||||
this(pathBaseClassLoader, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
/*--------------------------公有方法 start-------------------------------*/
|
||||
/**
|
||||
* 初始化设定文件
|
||||
*
|
||||
* @param groupedSetUrl 设定文件的URL
|
||||
* @param charset 字符集
|
||||
* @return 成功初始化与否
|
||||
*/
|
||||
public boolean init(URL groupedSetUrl, Charset charset) {
|
||||
if (groupedSetUrl == null) {
|
||||
throw new RuntimeException("Null GroupSet url or charset define!");
|
||||
}
|
||||
this.charset = charset;
|
||||
this.groupedSetUrl = groupedSetUrl;
|
||||
|
||||
return this.load(groupedSetUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载设置文件
|
||||
*
|
||||
* @param groupedSetUrl 配置文件URL
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
synchronized public boolean load(URL groupedSetUrl) {
|
||||
if (groupedSetUrl == null) {
|
||||
throw new RuntimeException("Null GroupSet url define!");
|
||||
}
|
||||
// log.debug("Load GroupSet file [{}]", groupedSetUrl.getPath());
|
||||
InputStream settingStream = null;
|
||||
try {
|
||||
settingStream = groupedSetUrl.openStream();
|
||||
load(settingStream);
|
||||
} catch (IOException e) {
|
||||
// log.error(e, "Load GroupSet error!");
|
||||
return false;
|
||||
} finally {
|
||||
IoUtil.close(settingStream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载配置文件
|
||||
*/
|
||||
public void reload() {
|
||||
this.load(groupedSetUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载设置文件。 此方法不会关闭流对象
|
||||
*
|
||||
* @param settingStream 文件流
|
||||
* @return 加载成功与否
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public boolean load(InputStream settingStream) throws IOException {
|
||||
super.clear();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = IoUtil.getReader(settingStream, charset);
|
||||
// 分组
|
||||
String group = null;
|
||||
LinkedHashSet<String> valueSet = null;
|
||||
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
line = line.trim();
|
||||
// 跳过注释行和空行
|
||||
if (StrUtil.isBlank(line) || line.startsWith(COMMENT_FLAG_PRE)) {
|
||||
// 空行和注释忽略
|
||||
continue;
|
||||
} else if (line.startsWith(StrUtil.BACKSLASH + COMMENT_FLAG_PRE)) {
|
||||
// 对于值中出现开头为#的字符串,需要转义处理,在此做反转义
|
||||
line = line.substring(1);
|
||||
}
|
||||
|
||||
// 记录分组名
|
||||
if (line.charAt(0) == GROUP_SURROUND[0] && line.charAt(line.length() - 1) == GROUP_SURROUND[1]) {
|
||||
// 开始新的分组取值,当出现重名分组时候,合并分组值
|
||||
group = line.substring(1, line.length() - 1).trim();
|
||||
valueSet = super.get(group);
|
||||
if (null == valueSet) {
|
||||
valueSet = new LinkedHashSet<String>();
|
||||
}
|
||||
super.put(group, valueSet);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加值
|
||||
if (null == valueSet) {
|
||||
// 当出现无分组值的时候,会导致valueSet为空,此时group为""
|
||||
valueSet = new LinkedHashSet<String>();
|
||||
super.put(StrUtil.EMPTY, valueSet);
|
||||
}
|
||||
valueSet.add(line);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获得设定文件的路径
|
||||
*/
|
||||
public String getPath() {
|
||||
return groupedSetUrl.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获得所有分组名
|
||||
*/
|
||||
public Set<String> getGroups() {
|
||||
return super.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得对应分组的所有值
|
||||
*
|
||||
* @param group 分组名
|
||||
* @return 分组的值集合
|
||||
*/
|
||||
public LinkedHashSet<String> getValues(String group) {
|
||||
if (group == null) {
|
||||
group = StrUtil.EMPTY;
|
||||
}
|
||||
return super.get(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在给定分组的集合中包含指定值<br>
|
||||
* 如果给定分组对应集合不存在,则返回false
|
||||
*
|
||||
* @param group 分组名
|
||||
* @param value 测试的值
|
||||
* @param otherValues 其他值
|
||||
* @return 是否包含
|
||||
*/
|
||||
public boolean contains(String group, String value, String... otherValues) {
|
||||
if (ArrayUtil.isNotEmpty(otherValues)) {
|
||||
// 需要测试多个值的情况
|
||||
final List<String> valueList = Arrays.asList(otherValues);
|
||||
valueList.add(value);
|
||||
return contains(group, valueList);
|
||||
} else {
|
||||
// 测试单个值
|
||||
final LinkedHashSet<String> valueSet = getValues(group);
|
||||
if (CollectionUtil.isEmpty(valueSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return valueSet.contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在给定分组的集合中全部包含指定值集合<br>
|
||||
* 如果给定分组对应集合不存在,则返回false
|
||||
*
|
||||
* @param group 分组名
|
||||
* @param values 测试的值集合
|
||||
* @return 是否包含
|
||||
*/
|
||||
public boolean contains(String group, Collection<String> values) {
|
||||
final LinkedHashSet<String> valueSet = getValues(group);
|
||||
if (CollectionUtil.isEmpty(values) || CollectionUtil.isEmpty(valueSet)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return valueSet.containsAll(values);
|
||||
}
|
||||
}
|
693
hutool-setting/src/main/java/cn/hutool/setting/Setting.java
Normal file
693
hutool-setting/src/main/java/cn/hutool/setting/Setting.java
Normal file
@@ -0,0 +1,693 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.FileResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.io.resource.UrlResource;
|
||||
import cn.hutool.core.io.watch.SimpleWatcher;
|
||||
import cn.hutool.core.io.watch.WatchMonitor;
|
||||
import cn.hutool.core.io.watch.WatchUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.log.StaticLog;
|
||||
import cn.hutool.setting.dialect.Props;
|
||||
|
||||
/**
|
||||
* 设置工具类。 用于支持设置(配置)文件<br>
|
||||
* BasicSetting用于替换Properties类,提供功能更加强大的配置文件,同时对Properties文件向下兼容
|
||||
*
|
||||
* <pre>
|
||||
* 1、支持变量,默认变量命名为 ${变量名},变量只能识别读入行的变量,例如第6行的变量在第三行无法读取
|
||||
* 2、支持分组,分组为中括号括起来的内容,中括号以下的行都为此分组的内容,无分组相当于空字符分组,若某个key是name,加上分组后的键相当于group.name
|
||||
* 3、注释以#开头,但是空行和不带“=”的行也会被跳过,但是建议加#
|
||||
* 4、store方法不会保存注释内容,慎重使用
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
public class Setting extends AbsSetting implements Map<String, String> {
|
||||
private static final long serialVersionUID = 3618305164959883393L;
|
||||
|
||||
/** 默认字符集 */
|
||||
public final static Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
||||
/** 默认配置文件扩展名 */
|
||||
public final static String EXT_NAME = "setting";
|
||||
|
||||
/** 附带分组的键值对存储 */
|
||||
private final GroupedMap groupedMap = new GroupedMap();
|
||||
|
||||
/** 本设置对象的字符集 */
|
||||
protected Charset charset;
|
||||
/** 是否使用变量 */
|
||||
protected boolean isUseVariable;
|
||||
/** 设定文件的URL */
|
||||
protected URL settingUrl;
|
||||
|
||||
private SettingLoader settingLoader;
|
||||
private WatchMonitor watchMonitor;
|
||||
|
||||
// ------------------------------------------------------------------------------------- Constructor start
|
||||
/**
|
||||
* 空构造
|
||||
*/
|
||||
public Setting() {
|
||||
this.charset = DEFAULT_CHARSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param path 相对路径或绝对路径
|
||||
*/
|
||||
public Setting(String path) {
|
||||
this(path, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param path 相对路径或绝对路径
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public Setting(String path, boolean isUseVariable) {
|
||||
this(path, DEFAULT_CHARSET, isUseVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用相对于Class文件根目录的相对路径
|
||||
*
|
||||
* @param path 相对路径或绝对路径
|
||||
* @param charset 字符集
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public Setting(String path, Charset charset, boolean isUseVariable) {
|
||||
Assert.notBlank(path, "Blank setting path !");
|
||||
this.init(ResourceUtil.getResourceObj(path), charset, isUseVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param configFile 配置文件对象
|
||||
* @param charset 字符集
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public Setting(File configFile, Charset charset, boolean isUseVariable) {
|
||||
Assert.notNull(configFile, "Null setting file define!");
|
||||
this.init(new FileResource(configFile), charset, isUseVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,相对于classes读取文件
|
||||
*
|
||||
* @param path 相对ClassPath路径或绝对路径
|
||||
* @param clazz 基准类
|
||||
* @param charset 字符集
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public Setting(String path, Class<?> clazz, Charset charset, boolean isUseVariable) {
|
||||
Assert.notBlank(path, "Blank setting path !");
|
||||
this.init(new ClassPathResource(path, clazz), charset, isUseVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param url 设定文件的URL
|
||||
* @param charset 字符集
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public Setting(URL url, Charset charset, boolean isUseVariable) {
|
||||
Assert.notNull(url, "Null setting url define!");
|
||||
this.init(new UrlResource(url), charset, isUseVariable);
|
||||
}
|
||||
// ------------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
/**
|
||||
* 初始化设定文件
|
||||
*
|
||||
* @param resource {@link Resource}
|
||||
* @param charset 字符集
|
||||
* @param isUseVariable 是否使用变量
|
||||
* @return 成功初始化与否
|
||||
*/
|
||||
public boolean init(Resource resource, Charset charset, boolean isUseVariable) {
|
||||
if (resource == null) {
|
||||
throw new NullPointerException("Null setting url define!");
|
||||
}
|
||||
this.settingUrl = resource.getUrl();
|
||||
this.charset = charset;
|
||||
this.isUseVariable = isUseVariable;
|
||||
|
||||
return load();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载配置文件
|
||||
*
|
||||
* @return 是否加载成功
|
||||
*/
|
||||
synchronized public boolean load() {
|
||||
if (null == this.settingLoader) {
|
||||
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable);
|
||||
}
|
||||
return settingLoader.load(new UrlResource(this.settingUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在配置文件变更时自动加载
|
||||
*
|
||||
* @param autoReload 是否自动加载
|
||||
*/
|
||||
public void autoLoad(boolean autoReload) {
|
||||
if (autoReload) {
|
||||
Assert.notNull(this.settingUrl, "Setting URL is null !");
|
||||
if (null != this.watchMonitor) {
|
||||
// 先关闭之前的监听
|
||||
this.watchMonitor.close();
|
||||
}
|
||||
this.watchMonitor = WatchUtil.createModify(this.settingUrl, new SimpleWatcher() {
|
||||
@Override
|
||||
public void onModify(WatchEvent<?> event, Path currentPath) {
|
||||
load();
|
||||
}
|
||||
});
|
||||
this.watchMonitor.start();
|
||||
StaticLog.debug("Auto load for [{}] listenning...", this.settingUrl);
|
||||
} else {
|
||||
IoUtil.close(this.watchMonitor);
|
||||
this.watchMonitor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获得设定文件的路径
|
||||
*/
|
||||
public String getSettingPath() {
|
||||
return (null == this.settingUrl) ? null : this.settingUrl.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 键值总数
|
||||
*
|
||||
* @return 键值总数
|
||||
*/
|
||||
public int size() {
|
||||
return this.groupedMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getByGroup(String key, String group) {
|
||||
return this.groupedMap.get(group, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
|
||||
*
|
||||
* @param keys 键列表,常用于别名
|
||||
* @return 值
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public Object getAndRemove(String... keys) {
|
||||
Object value = null;
|
||||
for (String key : keys) {
|
||||
value = remove(key);
|
||||
if (null != value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
|
||||
*
|
||||
* @param keys 键列表,常用于别名
|
||||
* @return 字符串值
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public String getAndRemoveStr(String... keys) {
|
||||
Object value = null;
|
||||
for (String key : keys) {
|
||||
value = remove(key);
|
||||
if (null != value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定分组的所有键值对,此方法获取的是原始键值对,获取的键值对可以被修改
|
||||
*
|
||||
* @param group 分组
|
||||
* @return map
|
||||
*/
|
||||
public Map<String, String> getMap(String group) {
|
||||
final LinkedHashMap<String, String> map = this.groupedMap.get(group);
|
||||
return (null != map) ? map : new LinkedHashMap<String, String>(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取group分组下所有配置键值对,组成新的{@link Setting}
|
||||
*
|
||||
* @param group 分组
|
||||
* @return {@link Setting}
|
||||
*/
|
||||
public Setting getSetting(String group) {
|
||||
final Setting setting = new Setting();
|
||||
setting.putAll(this.getMap(group));
|
||||
return setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取group分组下所有配置键值对,组成新的{@link Properties}
|
||||
*
|
||||
* @param group 分组
|
||||
* @return Properties对象
|
||||
*/
|
||||
public Properties getProperties(String group) {
|
||||
final Properties properties = new Properties();
|
||||
properties.putAll(getMap(group));
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取group分组下所有配置键值对,组成新的{@link Props}
|
||||
*
|
||||
* @param group 分组
|
||||
* @return Props对象
|
||||
* @since 4.1.21
|
||||
*/
|
||||
public Props getProps(String group) {
|
||||
final Props props = new Props();
|
||||
props.putAll(getMap(group));
|
||||
return props;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------- Functions
|
||||
/**
|
||||
* 持久化当前设置,会覆盖掉之前的设置<br>
|
||||
* 持久化不会保留之前的分组
|
||||
*
|
||||
* @param absolutePath 设置文件的绝对路径
|
||||
*/
|
||||
public void store(String absolutePath) {
|
||||
if (null == this.settingLoader) {
|
||||
settingLoader = new SettingLoader(this.groupedMap, this.charset, this.isUseVariable);
|
||||
}
|
||||
settingLoader.store(absolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Properties对象,原分组变为前缀
|
||||
*
|
||||
* @return Properties对象
|
||||
*/
|
||||
public Properties toProperties() {
|
||||
final Properties properties = new Properties();
|
||||
String group;
|
||||
for (Entry<String, LinkedHashMap<String, String>> groupEntry : this.groupedMap.entrySet()) {
|
||||
group = groupEntry.getKey();
|
||||
for (Entry<String, String> entry : groupEntry.getValue().entrySet()) {
|
||||
properties.setProperty(StrUtil.isEmpty(group) ? entry.getKey() : group + CharUtil.DOT + entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取GroupedMap
|
||||
*
|
||||
* @return GroupedMap
|
||||
* @since 4.0.12
|
||||
*/
|
||||
public GroupedMap getGroupedMap() {
|
||||
return this.groupedMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有分组
|
||||
*
|
||||
* @return 获得所有分组名
|
||||
*/
|
||||
public List<String> getGroups() {
|
||||
return CollUtil.newArrayList(this.groupedMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置变量的正则<br>
|
||||
* 正则只能有一个group表示变量本身,剩余为字符 例如 \$\{(name)\}表示${name}变量名为name的一个变量表示
|
||||
*
|
||||
* @param regex 正则
|
||||
*/
|
||||
public Setting setVarRegex(String regex) {
|
||||
if (null == this.settingLoader) {
|
||||
throw new NullPointerException("SettingLoader is null !");
|
||||
}
|
||||
this.settingLoader.setVarRegex(regex);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义字符编码
|
||||
*
|
||||
* @param charset 字符编码
|
||||
* @return this
|
||||
* @since 4.6.2
|
||||
*/
|
||||
public Setting setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Map interface with group
|
||||
/**
|
||||
* 某个分组对应的键值对是否为空
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 是否为空
|
||||
*/
|
||||
public boolean isEmpty(String group) {
|
||||
return this.groupedMap.isEmpty(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组中是否包含指定key
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 是否包含key
|
||||
*/
|
||||
public boolean containsKey(String group, String key) {
|
||||
return this.groupedMap.containsKey(group, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组中是否包含指定值
|
||||
*
|
||||
* @param group 分组
|
||||
* @param value 值
|
||||
* @return 是否包含值
|
||||
*/
|
||||
public boolean containsValue(String group, String value) {
|
||||
return this.groupedMap.containsValue(group, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分组对应的值,如果分组不存在或者值不存在则返回null
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 值,如果分组不存在或者值不存在则返回null
|
||||
*/
|
||||
public String get(String group, String key) {
|
||||
return this.groupedMap.get(group, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将键值对加入到对应分组中
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 此key之前存在的值,如果没有返回null
|
||||
*/
|
||||
public String put(String group, String key, String value) {
|
||||
return this.groupedMap.put(group, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定分组中删除指定值
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @return 被删除的值,如果值不存在,返回null
|
||||
*/
|
||||
public String remove(String group, Object key) {
|
||||
return this.groupedMap.remove(group, Convert.toStr(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入多个键值对到某个分组下
|
||||
*
|
||||
* @param group 分组
|
||||
* @param m 键值对
|
||||
* @return this
|
||||
*/
|
||||
public Setting putAll(String group, Map<? extends String, ? extends String> m) {
|
||||
this.groupedMap.putAll(group, m);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定分组下的所有键值对
|
||||
*
|
||||
* @param group 分组
|
||||
* @return this
|
||||
*/
|
||||
public Setting clear(String group) {
|
||||
this.groupedMap.clear(group);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组所有键的Set
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 键Set
|
||||
*/
|
||||
public Set<String> keySet(String group) {
|
||||
return this.groupedMap.keySet(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组下所有值
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 值
|
||||
*/
|
||||
public Collection<String> values(String group) {
|
||||
return this.groupedMap.values(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定分组下所有键值对
|
||||
*
|
||||
* @param group 分组
|
||||
* @return 键值对
|
||||
*/
|
||||
public Set<Entry<String, String>> entrySet(String group) {
|
||||
return this.groupedMap.entrySet(group);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return this
|
||||
* @since 3.3.1
|
||||
*/
|
||||
public Setting set(String key, String value) {
|
||||
this.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将键值对加入到对应分组中
|
||||
*
|
||||
* @param group 分组
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 此key之前存在的值,如果没有返回null
|
||||
*/
|
||||
public Setting set(String group, String key, String value) {
|
||||
this.put(group, key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Override Map interface
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.groupedMap.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认分组(空分组)中是否包含指定key对应的值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 默认分组中是否包含指定key对应的值
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.groupedMap.containsKey(DEFAULT_GROUP, Convert.toStr(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认分组(空分组)中是否包含指定值
|
||||
*
|
||||
* @param value 值
|
||||
* @return 默认分组中是否包含指定值
|
||||
*/
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.groupedMap.containsValue(DEFAULT_GROUP, Convert.toStr(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认分组(空分组)中指定key对应的值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 默认分组(空分组)中指定key对应的值
|
||||
*/
|
||||
@Override
|
||||
public String get(Object key) {
|
||||
return this.groupedMap.get(DEFAULT_GROUP, Convert.toStr(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定键值对加入到默认分组(空分组)中
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return 加入的值
|
||||
*/
|
||||
@Override
|
||||
public String put(String key, String value) {
|
||||
return this.groupedMap.put(DEFAULT_GROUP, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除默认分组(空分组)中指定值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 移除的值
|
||||
*/
|
||||
@Override
|
||||
public String remove(Object key) {
|
||||
return this.groupedMap.remove(DEFAULT_GROUP, Convert.toStr(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将键值对Map加入默认分组(空分组)中
|
||||
*
|
||||
* @param m Map
|
||||
*/
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends String> m) {
|
||||
this.groupedMap.putAll(DEFAULT_GROUP, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空默认分组(空分组)中的所有键值对
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
this.groupedMap.clear(DEFAULT_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认分组(空分组)中的所有键列表
|
||||
*
|
||||
* @return 默认分组(空分组)中的所有键列表
|
||||
*/
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return this.groupedMap.keySet(DEFAULT_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认分组(空分组)中的所有值列表
|
||||
*
|
||||
* @return 默认分组(空分组)中的所有值列表
|
||||
*/
|
||||
@Override
|
||||
public Collection<String> values() {
|
||||
return this.groupedMap.values(DEFAULT_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认分组(空分组)中的所有键值对列表
|
||||
*
|
||||
* @return 默认分组(空分组)中的所有键值对列表
|
||||
*/
|
||||
@Override
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return this.groupedMap.entrySet(DEFAULT_GROUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((charset == null) ? 0 : charset.hashCode());
|
||||
result = prime * result + ((groupedMap == null) ? 0 : groupedMap.hashCode());
|
||||
result = prime * result + (isUseVariable ? 1231 : 1237);
|
||||
result = prime * result + ((settingUrl == null) ? 0 : settingUrl.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Setting other = (Setting) obj;
|
||||
if (charset == null) {
|
||||
if (other.charset != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (false == charset.equals(other.charset)) {
|
||||
return false;
|
||||
}
|
||||
if (groupedMap == null) {
|
||||
if (other.groupedMap != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (false == groupedMap.equals(other.groupedMap)) {
|
||||
return false;
|
||||
}
|
||||
if (isUseVariable != other.isUseVariable) {
|
||||
return false;
|
||||
}
|
||||
if (settingUrl == null) {
|
||||
if (other.settingUrl != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!settingUrl.equals(other.settingUrl)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return groupedMap.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,225 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.UrlResource;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import cn.hutool.log.LogFactory;
|
||||
|
||||
/**
|
||||
* Setting文件加载器
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class SettingLoader {
|
||||
private static Log log = LogFactory.get();
|
||||
|
||||
/** 注释符号(当有此符号在行首,表示此行为注释) */
|
||||
private final static char COMMENT_FLAG_PRE = '#';
|
||||
/** 赋值分隔符(用于分隔键值对) */
|
||||
private final static char ASSIGN_FLAG = '=';
|
||||
/** 变量名称的正则 */
|
||||
private String reg_var = "\\$\\{(.*?)\\}";
|
||||
|
||||
/** 本设置对象的字符集 */
|
||||
private Charset charset;
|
||||
/** 是否使用变量 */
|
||||
private boolean isUseVariable;
|
||||
/** GroupedMap */
|
||||
private GroupedMap groupedMap;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param groupedMap GroupedMap
|
||||
*/
|
||||
public SettingLoader(GroupedMap groupedMap) {
|
||||
this(groupedMap, CharsetUtil.CHARSET_UTF_8, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param groupedMap GroupedMap
|
||||
* @param charset 编码
|
||||
* @param isUseVariable 是否使用变量
|
||||
*/
|
||||
public SettingLoader(GroupedMap groupedMap, Charset charset, boolean isUseVariable) {
|
||||
this.groupedMap = groupedMap;
|
||||
this.charset = charset;
|
||||
this.isUseVariable = isUseVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载设置文件
|
||||
*
|
||||
* @param urlResource 配置文件URL
|
||||
* @return 加载是否成功
|
||||
*/
|
||||
public boolean load(UrlResource urlResource) {
|
||||
if (urlResource == null) {
|
||||
throw new NullPointerException("Null setting url define!");
|
||||
}
|
||||
log.debug("Load setting file [{}]", urlResource);
|
||||
InputStream settingStream = null;
|
||||
try {
|
||||
settingStream = urlResource.getStream();
|
||||
load(settingStream);
|
||||
} catch (Exception e) {
|
||||
log.error(e, "Load setting error!");
|
||||
return false;
|
||||
} finally {
|
||||
IoUtil.close(settingStream);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载设置文件。 此方法不会关闭流对象
|
||||
*
|
||||
* @param settingStream 文件流
|
||||
* @return 加载成功与否
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
synchronized public boolean load(InputStream settingStream) throws IOException {
|
||||
this.groupedMap.clear();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = IoUtil.getReader(settingStream, this.charset);
|
||||
// 分组
|
||||
String group = null;
|
||||
|
||||
String line;
|
||||
while (true) {
|
||||
line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
line = line.trim();
|
||||
// 跳过注释行和空行
|
||||
if (StrUtil.isBlank(line) || StrUtil.startWith(line, COMMENT_FLAG_PRE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 记录分组名
|
||||
if (StrUtil.isSurround(line, CharUtil.BRACKET_START, CharUtil.BRACKET_END)) {
|
||||
group = line.substring(1, line.length() - 1).trim();
|
||||
continue;
|
||||
}
|
||||
|
||||
final String[] keyValue = StrUtil.splitToArray(line, ASSIGN_FLAG, 2);
|
||||
// 跳过不符合键值规范的行
|
||||
if (keyValue.length < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = keyValue[1].trim();
|
||||
// 替换值中的所有变量变量(变量必须是此行之前定义的变量,否则无法找到)
|
||||
if (this.isUseVariable) {
|
||||
value = replaceVar(group, value);
|
||||
}
|
||||
this.groupedMap.put(group, keyValue[0].trim(), value);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置变量的正则<br>
|
||||
* 正则只能有一个group表示变量本身,剩余为字符 例如 \$\{(name)\}表示${name}变量名为name的一个变量表示
|
||||
*
|
||||
* @param regex 正则
|
||||
*/
|
||||
public void setVarRegex(String regex) {
|
||||
this.reg_var = regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化当前设置,会覆盖掉之前的设置<br>
|
||||
* 持久化会不会保留之前的分组
|
||||
*
|
||||
* @param absolutePath 设置文件的绝对路径
|
||||
*/
|
||||
public void store(String absolutePath) {
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
writer = FileUtil.getPrintWriter(absolutePath, charset, false);
|
||||
store(writer);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e, "Store Setting to [{}] error!", absolutePath);
|
||||
} finally {
|
||||
IoUtil.close(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储到Writer
|
||||
*
|
||||
* @param writer Writer
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
synchronized private void store(PrintWriter writer) throws IOException {
|
||||
for (Entry<String, LinkedHashMap<String, String>> groupEntry : this.groupedMap.entrySet()) {
|
||||
writer.println(StrUtil.format("{}{}{}", CharUtil.BRACKET_START, groupEntry.getKey(), CharUtil.BRACKET_END));
|
||||
for (Entry<String, String> entry : groupEntry.getValue().entrySet()) {
|
||||
writer.println(StrUtil.format("{} {} {}", entry.getKey(), ASSIGN_FLAG, entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------- Private method start
|
||||
/**
|
||||
* 替换给定值中的变量标识
|
||||
*
|
||||
* @param group 所在分组
|
||||
* @param value 值
|
||||
* @return 替换后的字符串
|
||||
*/
|
||||
private String replaceVar(String group, String value) {
|
||||
// 找到所有变量标识
|
||||
final Set<String> vars = ReUtil.findAll(reg_var, value, 0, new HashSet<String>());
|
||||
String key;
|
||||
for (String var : vars) {
|
||||
key = ReUtil.get(reg_var, var, 1);
|
||||
if (StrUtil.isNotBlank(key)) {
|
||||
// 查找变量名对应的值
|
||||
String varValue = this.groupedMap.get(group, key);
|
||||
if (null != varValue) {
|
||||
// 替换标识
|
||||
value = value.replace(var, varValue);
|
||||
} else {
|
||||
// 跨分组查找
|
||||
final List<String> groupAndKey = StrUtil.split(key, CharUtil.DOT, 2);
|
||||
if (groupAndKey.size() > 1) {
|
||||
varValue = this.groupedMap.get(groupAndKey.get(0), groupAndKey.get(1));
|
||||
if (null != varValue) {
|
||||
// 替换标识
|
||||
value = value.replace(var, varValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------- Private method end
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 设置异常
|
||||
* @author xiaoleilu
|
||||
*/
|
||||
public class SettingRuntimeException extends RuntimeException{
|
||||
private static final long serialVersionUID = 7941096116780378387L;
|
||||
|
||||
public SettingRuntimeException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
public SettingRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SettingRuntimeException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public SettingRuntimeException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public SettingRuntimeException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package cn.hutool.setting;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* Setting工具类<br>
|
||||
* 提供静态方法获取配置文件
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
public class SettingUtil {
|
||||
/** 配置文件缓存 */
|
||||
private static Map<String, Setting> settingMap = new ConcurrentHashMap<>();
|
||||
private static Object lock = new Object();
|
||||
|
||||
/**
|
||||
* 获取当前环境下的配置文件<br>
|
||||
* name可以为不包括扩展名的文件名(默认.setting为结尾),也可以是文件名全称
|
||||
*
|
||||
* @param name 文件名,如果没有扩展名,默认为.setting
|
||||
* @return 当前环境下配置文件
|
||||
*/
|
||||
public static Setting get(String name) {
|
||||
Setting setting = settingMap.get(name);
|
||||
if (null == setting) {
|
||||
synchronized (lock) {
|
||||
setting = settingMap.get(name);
|
||||
if (null == setting) {
|
||||
String filePath = name;
|
||||
String extName = FileUtil.extName(filePath);
|
||||
if(StrUtil.isEmpty(extName)) {
|
||||
filePath = filePath + "." + Setting.EXT_NAME;
|
||||
}
|
||||
setting = new Setting(filePath, true);
|
||||
settingMap.put(name, setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
}
|
@@ -0,0 +1,514 @@
|
||||
package cn.hutool.setting.dialect;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.getter.BasicTypeGetter;
|
||||
import cn.hutool.core.getter.OptBasicTypeGetter;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.FileResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.io.resource.UrlResource;
|
||||
import cn.hutool.core.io.watch.SimpleWatcher;
|
||||
import cn.hutool.core.io.watch.WatchMonitor;
|
||||
import cn.hutool.core.io.watch.WatchUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.log.Log;
|
||||
import cn.hutool.log.LogFactory;
|
||||
import cn.hutool.setting.SettingRuntimeException;
|
||||
|
||||
/**
|
||||
* Properties文件读取封装类
|
||||
*
|
||||
* @author loolly
|
||||
*/
|
||||
public final class Props extends Properties implements BasicTypeGetter<String>, OptBasicTypeGetter<String> {
|
||||
private static final long serialVersionUID = 1935981579709590740L;
|
||||
private final static Log log = LogFactory.get();
|
||||
|
||||
// ----------------------------------------------------------------------- 私有属性 start
|
||||
/** 属性文件的URL */
|
||||
private URL propertiesFileUrl;
|
||||
private WatchMonitor watchMonitor;
|
||||
/** properties文件编码 */
|
||||
private Charset charset = CharsetUtil.CHARSET_ISO_8859_1;
|
||||
// ----------------------------------------------------------------------- 私有属性 end
|
||||
|
||||
/**
|
||||
* 获得Classpath下的Properties文件
|
||||
*
|
||||
* @param resource 资源(相对Classpath的路径)
|
||||
* @return Properties
|
||||
*/
|
||||
public static Properties getProp(String resource) {
|
||||
return new Props(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Classpath下的Properties文件
|
||||
*
|
||||
* @param resource 资源(相对Classpath的路径)
|
||||
* @param charsetName 字符集
|
||||
* @return Properties
|
||||
*/
|
||||
public static Properties getProp(String resource, String charsetName) {
|
||||
return new Props(resource, charsetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Classpath下的Properties文件
|
||||
*
|
||||
* @param resource 资源(相对Classpath的路径)
|
||||
* @param charset 字符集
|
||||
* @return Properties
|
||||
*/
|
||||
public static Properties getProp(String resource, Charset charset) {
|
||||
return new Props(resource, charset);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- 构造方法 start
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public Props() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用相对于Class文件根目录的相对路径
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
public Props(String path) {
|
||||
this(path, CharsetUtil.CHARSET_ISO_8859_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用相对于Class文件根目录的相对路径
|
||||
*
|
||||
* @param path 相对或绝对路径
|
||||
* @param charsetName 字符集
|
||||
*/
|
||||
public Props(String path, String charsetName) {
|
||||
this(path, CharsetUtil.charset(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用相对于Class文件根目录的相对路径
|
||||
*
|
||||
* @param path 相对或绝对路径
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public Props(String path, Charset charset) {
|
||||
Assert.notBlank(path, "Blank properties file path !");
|
||||
if(null != charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
this.load(ResourceUtil.getResourceObj(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param propertiesFile 配置文件对象
|
||||
*/
|
||||
public Props(File propertiesFile) {
|
||||
this(propertiesFile, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param propertiesFile 配置文件对象
|
||||
* @param charsetName 字符集
|
||||
*/
|
||||
public Props(File propertiesFile, String charsetName) {
|
||||
this(propertiesFile, Charset.forName(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param propertiesFile 配置文件对象
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public Props(File propertiesFile, Charset charset) {
|
||||
Assert.notNull(propertiesFile, "Null properties file!");
|
||||
this.charset = charset;
|
||||
this.load(new FileResource(propertiesFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,相对于classes读取文件
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @param clazz 基准类
|
||||
*/
|
||||
public Props(String path, Class<?> clazz) {
|
||||
this(path, clazz, CharsetUtil.ISO_8859_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,相对于classes读取文件
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @param clazz 基准类
|
||||
* @param charsetName 字符集
|
||||
*/
|
||||
public Props(String path, Class<?> clazz, String charsetName) {
|
||||
this(path, clazz, CharsetUtil.charset(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,相对于classes读取文件
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @param clazz 基准类
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public Props(String path, Class<?> clazz, Charset charset) {
|
||||
Assert.notBlank(path, "Blank properties file path !");
|
||||
if(null != charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
this.load(new ClassPathResource(path, clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用URL读取
|
||||
*
|
||||
* @param propertiesUrl 属性文件路径
|
||||
*/
|
||||
public Props(URL propertiesUrl) {
|
||||
this(propertiesUrl, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用URL读取
|
||||
*
|
||||
* @param propertiesUrl 属性文件路径
|
||||
* @param charsetName 字符集
|
||||
*/
|
||||
public Props(URL propertiesUrl, String charsetName) {
|
||||
this(propertiesUrl, CharsetUtil.charset(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用URL读取
|
||||
*
|
||||
* @param propertiesUrl 属性文件路径
|
||||
* @param charset 字符集
|
||||
*/
|
||||
public Props(URL propertiesUrl, Charset charset) {
|
||||
Assert.notNull(propertiesUrl, "Null properties URL !");
|
||||
if(null != charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
this.load(new UrlResource(propertiesUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用URL读取
|
||||
*
|
||||
* @param properties 属性文件路径
|
||||
*/
|
||||
public Props(Properties properties) {
|
||||
if (CollectionUtil.isNotEmpty(properties)) {
|
||||
this.putAll(properties);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- 构造方法 end
|
||||
|
||||
/**
|
||||
* 初始化配置文件
|
||||
*
|
||||
* @param urlResource {@link UrlResource}
|
||||
*/
|
||||
public void load(Resource urlResource) {
|
||||
this.propertiesFileUrl = urlResource.getUrl();
|
||||
if (null == this.propertiesFileUrl) {
|
||||
throw new SettingRuntimeException("Can not find properties file: [{}]", urlResource);
|
||||
}
|
||||
log.debug("Load properties [{}]", propertiesFileUrl.getPath());
|
||||
try (final BufferedReader reader = urlResource.getReader(charset)) {
|
||||
super.load(reader);
|
||||
} catch (Exception e) {
|
||||
log.error(e, "Load properties error!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载配置文件
|
||||
*/
|
||||
public void load() {
|
||||
this.load(new UrlResource(this.propertiesFileUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在配置文件变更时自动加载
|
||||
*
|
||||
* @param autoReload 是否自动加载
|
||||
*/
|
||||
public void autoLoad(boolean autoReload) {
|
||||
if (autoReload) {
|
||||
Assert.notNull(this.propertiesFileUrl, "Properties URL is null !");
|
||||
if (null != this.watchMonitor) {
|
||||
// 先关闭之前的监听
|
||||
this.watchMonitor.close();
|
||||
}
|
||||
this.watchMonitor = WatchUtil.createModify(this.propertiesFileUrl, new SimpleWatcher(){
|
||||
@Override
|
||||
public void onModify(WatchEvent<?> event, Path currentPath) {
|
||||
load();
|
||||
}
|
||||
});
|
||||
this.watchMonitor.start();
|
||||
} else {
|
||||
IoUtil.close(this.watchMonitor);
|
||||
this.watchMonitor = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- Get start
|
||||
@Override
|
||||
public Object getObj(String key, Object defaultValue) {
|
||||
return getStr(key, null == defaultValue ? null : defaultValue.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObj(String key) {
|
||||
return getObj(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStr(String key, String defaultValue) {
|
||||
return super.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStr(String key) {
|
||||
return super.getProperty(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt(String key, Integer defaultValue) {
|
||||
return Convert.toInt(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInt(String key) {
|
||||
return getInt(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBool(String key, Boolean defaultValue) {
|
||||
return Convert.toBool(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBool(String key) {
|
||||
return getBool(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
return Convert.toLong(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key) {
|
||||
return getLong(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getChar(String key, Character defaultValue) {
|
||||
final String value = getStr(key);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Character getChar(String key) {
|
||||
return getChar(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getFloat(String key) {
|
||||
return getFloat(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float getFloat(String key, Float defaultValue) {
|
||||
return Convert.toFloat(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDouble(String key, Double defaultValue) throws NumberFormatException {
|
||||
return Convert.toDouble(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDouble(String key) throws NumberFormatException {
|
||||
return getDouble(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getShort(String key, Short defaultValue) {
|
||||
return Convert.toShort(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Short getShort(String key) {
|
||||
return getShort(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getByte(String key, Byte defaultValue) {
|
||||
return Convert.toByte(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Byte getByte(String key) {
|
||||
return getByte(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
|
||||
final String valueStr = getStr(key);
|
||||
if (StrUtil.isBlank(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return new BigDecimal(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBigDecimal(String key) {
|
||||
return getBigDecimal(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getBigInteger(String key, BigInteger defaultValue) {
|
||||
final String valueStr = getStr(key);
|
||||
if (StrUtil.isBlank(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
return new BigInteger(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getBigInteger(String key) {
|
||||
return getBigInteger(key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnum(Class<E> clazz, String key, E defaultValue) {
|
||||
return Convert.toEnum(clazz, getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) {
|
||||
return getEnum(clazz, key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate(String key, Date defaultValue) {
|
||||
return Convert.toDate(getStr(key), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDate(String key) {
|
||||
return getDate(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
|
||||
*
|
||||
* @param keys 键列表,常用于别名
|
||||
* @return 字符串值
|
||||
* @since 4.1.21
|
||||
*/
|
||||
public String getAndRemoveStr(String... keys) {
|
||||
Object value = null;
|
||||
for (String key : keys) {
|
||||
value = remove(key);
|
||||
if (null != value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- Get end
|
||||
|
||||
// ----------------------------------------------------------------------- Set start
|
||||
/**
|
||||
* 设置值,无给定键创建之。设置后未持久化
|
||||
*
|
||||
* @param key 属性键
|
||||
* @param value 属性值
|
||||
*/
|
||||
public void setProperty(String key, Object value) {
|
||||
super.setProperty(key, value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 持久化当前设置,会覆盖掉之前的设置
|
||||
*
|
||||
* @param absolutePath 设置文件的绝对路径
|
||||
* @throws IORuntimeException IO异常,可能为文件未找到
|
||||
*/
|
||||
public void store(String absolutePath) throws IORuntimeException{
|
||||
Writer writer = null;
|
||||
try {
|
||||
writer = FileUtil.getWriter(absolutePath, charset, false);
|
||||
super.store(writer, null);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e, "Store properties to [{}] error!", absolutePath);
|
||||
} finally {
|
||||
IoUtil.close(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储当前设置,会覆盖掉以前的设置
|
||||
*
|
||||
* @param path 相对路径
|
||||
* @param clazz 相对的类
|
||||
*/
|
||||
public void store(String path, Class<?> clazz) {
|
||||
this.store(FileUtil.getAbsolutePath(path, clazz));
|
||||
}
|
||||
// ----------------------------------------------------------------------- Set end
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 配置文件实现分装,例如Properties封装Props
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.setting.dialect;
|
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Setting模块主要针对Properties文件读写做封装,同时定义一套自己的配置文件规范,实现兼容性良好的配置工具。
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.setting;
|
@@ -0,0 +1,36 @@
|
||||
package cn.hutool.setting.profile;
|
||||
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import cn.hutool.setting.Setting;
|
||||
|
||||
/**
|
||||
* 全局的Profile配置中心
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class GlobalProfile {
|
||||
|
||||
private GlobalProfile() {
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------- Static method start
|
||||
/**
|
||||
* 设置全局环境
|
||||
* @param profile 环境
|
||||
* @return {@link Profile}
|
||||
*/
|
||||
public static Profile setProfile(String profile) {
|
||||
return Singleton.get(Profile.class, profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得全局的当前环境下对应的配置文件
|
||||
* @param settingName 配置文件名,可以忽略默认后者(.setting)
|
||||
* @return {@link Setting}
|
||||
*/
|
||||
public static Setting getSetting(String settingName) {
|
||||
return Singleton.get(Profile.class).getSetting(settingName);
|
||||
}
|
||||
// -------------------------------------------------------------------------------- Static method end
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
package cn.hutool.setting.profile;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.setting.Setting;
|
||||
|
||||
/**
|
||||
* Profile可以让我们定义一系列的配置信息,然后指定其激活条件。<br>
|
||||
* 此类中我们规范一套规则如下:<br>
|
||||
* 默认的,我们读取${classpath}/default下的配置文件(*.setting文件),当调用setProfile方法时,指定一个profile,即可读取其目录下的配置文件。<br>
|
||||
* 比如我们定义几个profile:test,develop,production,分别代表测试环境、开发环境和线上环境,我希望读取数据库配置文件db.setting,那么:
|
||||
* <ol>
|
||||
* <li>test =》 ${classpath}/test/db.setting</li>
|
||||
* <li>develop =》 ${classpath}/develop/db.setting</li>
|
||||
* <li>production =》 ${classpath}/production/db.setting</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Looly
|
||||
*
|
||||
*/
|
||||
public class Profile implements Serializable {
|
||||
private static final long serialVersionUID = -4189955219454008744L;
|
||||
|
||||
/** 默认环境 */
|
||||
public static final String DEFAULT_PROFILE = "default";
|
||||
|
||||
/** 条件 */
|
||||
private String profile;
|
||||
/** 编码 */
|
||||
private Charset charset;
|
||||
/** 是否使用变量 */
|
||||
private boolean useVar;
|
||||
/** 配置文件缓存 */
|
||||
private Map<String, Setting> settingMap = new ConcurrentHashMap<>();
|
||||
|
||||
// -------------------------------------------------------------------------------- Constructor start
|
||||
/**
|
||||
* 默认构造,环境使用默认的:default,编码UTF-8,不使用变量
|
||||
*/
|
||||
public Profile() {
|
||||
this(DEFAULT_PROFILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,编码UTF-8,不使用变量
|
||||
*
|
||||
* @param profile 环境
|
||||
*/
|
||||
public Profile(String profile) {
|
||||
this(profile, Setting.DEFAULT_CHARSET, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param profile 环境
|
||||
* @param charset 编码
|
||||
* @param useVar 是否使用变量
|
||||
*/
|
||||
public Profile(String profile, Charset charset, boolean useVar) {
|
||||
super();
|
||||
this.profile = profile;
|
||||
this.charset = charset;
|
||||
this.useVar = useVar;
|
||||
}
|
||||
// -------------------------------------------------------------------------------- Constructor end
|
||||
|
||||
/**
|
||||
* 获取当前环境下的配置文件
|
||||
*
|
||||
* @param name 文件名,如果没有扩展名,默认为.setting
|
||||
* @return 当前环境下配置文件
|
||||
*/
|
||||
public Setting getSetting(String name) {
|
||||
String nameForProfile = fixNameForProfile(name);
|
||||
Setting setting = settingMap.get(nameForProfile);
|
||||
if (null == setting) {
|
||||
setting = new Setting(nameForProfile, this.charset, this.useVar);
|
||||
settingMap.put(nameForProfile, setting);
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置环境
|
||||
*
|
||||
* @param profile 环境
|
||||
* @return 自身
|
||||
*/
|
||||
public Profile setProfile(String profile) {
|
||||
this.profile = profile;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 自身
|
||||
*/
|
||||
public Profile setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否使用变量
|
||||
*
|
||||
* @param useVar 变量
|
||||
* @return 自身
|
||||
*/
|
||||
public Profile setUseVar(boolean useVar) {
|
||||
this.useVar = useVar;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有环境的配置文件
|
||||
*
|
||||
* @return 自身
|
||||
*/
|
||||
public Profile clear() {
|
||||
this.settingMap.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------- Private method start
|
||||
/**
|
||||
* 修正文件名
|
||||
*
|
||||
* @param name 文件名
|
||||
* @return 修正后的文件名
|
||||
*/
|
||||
private String fixNameForProfile(String name) {
|
||||
Assert.notBlank(name, "Setting name must be not blank !");
|
||||
final String actralProfile = StrUtil.nullToEmpty(this.profile);
|
||||
if (false == name.contains(StrUtil.DOT)) {
|
||||
return StrUtil.format("{}/{}.setting", actralProfile, name);
|
||||
}
|
||||
return StrUtil.format("{}/{}", actralProfile, name);
|
||||
}
|
||||
// -------------------------------------------------------------------------------- Private method end
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 配置文件实现分装,例如Properties封装Props
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.setting.profile;
|
Reference in New Issue
Block a user