org.dromara.hutool.setting.props.Props Maven / Gradle / Ivy
/*
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.hutool.setting.props;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.func.LambdaInfo;
import org.dromara.hutool.core.func.LambdaUtil;
import org.dromara.hutool.core.func.SerFunction;
import org.dromara.hutool.core.func.SerSupplier;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.io.resource.Resource;
import org.dromara.hutool.core.io.resource.ResourceUtil;
import org.dromara.hutool.core.io.watch.WatchMonitor;
import org.dromara.hutool.core.io.watch.WatchUtil;
import org.dromara.hutool.core.io.watch.watchers.SimpleWatcher;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.getter.TypeGetter;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.reflect.ConstructorUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.util.CharsetUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.log.LogUtil;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.util.Arrays;
import java.util.Properties;
/**
* Properties文件读取封装类
*
* @author loolly
*/
public final class Props extends Properties implements TypeGetter {
private static final long serialVersionUID = 1935981579709590740L;
/**
* 默认配置文件扩展名
*/
public final static String EXT_NAME = "properties";
/**
* 构建一个空的Props,用于手动加入参数
*
* @return Setting
* @since 5.4.3
*/
public static Props of() {
return new Props();
}
// ----------------------------------------------------------------------- 私有属性 start
/**
* 属性文件的Resource
*/
private Resource resource;
private WatchMonitor watchMonitor;
/**
* properties文件编码
* issue#1701,此属性不能被序列化,故忽略序列化
*/
private transient Charset charset = CharsetUtil.ISO_8859_1;
// ----------------------------------------------------------------------- 私有属性 end
/**
* 获得Classpath下的Properties文件
*
* @param resource 资源(相对Classpath的路径)
* @return Props
*/
public static Props of(final String resource) {
return new Props(resource);
}
/**
* 获得Classpath下的Properties文件
*
* @param resource 资源(相对Classpath的路径)
* @param charset 自定义编码
* @return Properties
*/
public static Props of(final String resource, final Charset charset) {
return new Props(resource, charset);
}
/**
* {@link Properties}转为Props
*
* @param properties {@link Properties}
* @return Props
*/
public static Props of(final Properties properties) {
return new Props(properties);
}
// ----------------------------------------------------------------------- 构造方法 start
/**
* 构造
*/
public Props() {
}
/**
* 构造,使用相对于Class文件根目录的相对路径
*
* @param path 配置文件路径,相对于ClassPath,或者使用绝对路径
*/
public Props(final String path) {
this(path, null);
}
/**
* 构造,使用相对于Class文件根目录的相对路径
*
* @param path 相对或绝对路径
* @param charset 自定义编码
*/
public Props(final String path, final Charset charset) {
Assert.notBlank(path, "Blank properties file path !");
if (null != charset) {
this.charset = charset;
}
this.load(ResourceUtil.getResource(path));
}
/**
* 构造
*
* @param propertiesFile 配置文件对象
*/
public Props(final File propertiesFile) {
this(propertiesFile, null);
}
/**
* 构造
*
* @param propertiesFile 配置文件对象
* @param charset 自定义编码
*/
public Props(final File propertiesFile, final Charset charset) {
Assert.notNull(propertiesFile, "Null properties file!");
if (null != charset) {
this.charset = charset;
}
this.load(ResourceUtil.getResource(propertiesFile));
}
/**
* 构造,使用URL读取
*
* @param resource {@link Resource}
* @param charset 自定义编码
*/
public Props(final Resource resource, final Charset charset) {
Assert.notNull(resource, "Null properties URL !");
if (null != charset) {
this.charset = charset;
}
this.load(resource);
}
/**
* 构造,使用URL读取
*
* @param properties 属性文件路径
*/
public Props(final Properties properties) {
if (MapUtil.isNotEmpty(properties)) {
this.putAll(properties);
}
}
// ----------------------------------------------------------------------- 构造方法 end
/**
* 初始化配置文件
*
* @param url {@link URL}
* @since 5.5.2
*/
public void load(final URL url) {
load(ResourceUtil.getResource(url));
}
/**
* 初始化配置文件
*
* @param resource {@link Resource}
*/
public void load(final Resource resource) {
Assert.notNull(resource, "Props resource must be not null!");
this.resource = resource;
ResourceUtil.loadTo(this, resource, this.charset);
}
/**
* 重新加载配置文件
*/
public void load() {
this.load(this.resource);
}
/**
* 在配置文件变更时自动加载
*
* @param autoReload 是否自动加载
*/
public void autoLoad(final boolean autoReload) {
if (autoReload) {
Assert.notNull(this.resource, "Properties resource must be not null!");
// 先关闭之前的监听
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = WatchUtil.ofModify(this.resource.getUrl(), new SimpleWatcher() {
private static final long serialVersionUID = 1L;
@Override
public void onModify(final WatchEvent event, final WatchKey key) {
load();
}
});
this.watchMonitor.start();
} else {
IoUtil.closeQuietly(this.watchMonitor);
this.watchMonitor = null;
}
}
@Override
public Object getObj(final CharSequence key, final Object defaultValue) {
Assert.notNull(key, "Key must be not null!");
return ObjUtil.defaultIfNull(getProperty(key.toString()), defaultValue);
}
/**
* 根据lambda的方法引用,获取
*
* @param func 方法引用
* @param 参数类型
* @param 返回值类型
* @return 获取表达式对应属性和返回的对象
*/
public T get(final SerFunction
func) {
final LambdaInfo lambdaInfo = LambdaUtil.resolve(func);
return get(lambdaInfo.getFieldName(), lambdaInfo.getReturnType());
}
/**
* 获取并删除键值对,当指定键对应值非空时,返回并删除这个值,后边的键对应的值不再查找
*
* @param keys 键列表,常用于别名
* @return 字符串值
* @since 4.1.21
*/
public String getAndRemoveStr(final String... keys) {
Object value = null;
for (final String key : keys) {
value = remove(key);
if (null != value) {
break;
}
}
return (String) value;
}
/**
* 获取一个新的子属性,子属性键值对拥有公共前缀,以.分隔。
*
* a.b
* a.c
* b.a
*
* 则调用getSubProps("a");得到
*
* a.b
* a.c
*
*
* @param prefix 前缀,可以不以.结尾
* @return 子属性
*/
public Props getSubProps(final String prefix) {
final Props subProps = new Props();
final String finalPrefix = StrUtil.addSuffixIfNot(prefix, StrUtil.DOT);
final int prefixLength = finalPrefix.length();
forEach((key, value) -> {
final String keyStr = key.toString();
if (StrUtil.startWith(keyStr, finalPrefix)) {
subProps.set(StrUtil.subSuf(keyStr, prefixLength), value);
}
});
return subProps;
}
/**
* 转换为标准的{@link Properties}对象
*
* @return {@link Properties}对象
* @since 5.7.4
*/
public Properties toProperties() {
final Properties properties = new Properties();
properties.putAll(this);
return properties;
}
/**
* 将配置文件转换为Bean,支持嵌套Bean
* 支持的表达式:
*
*
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
*
*
* @param Bean类型
* @param beanClass Bean类
* @return Bean对象
* @since 4.6.3
*/
public T toBean(final Class beanClass) {
return toBean(beanClass, null);
}
/**
* 将配置文件转换为Bean,支持嵌套Bean
* 支持的表达式:
*
*
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
*
*
* @param Bean类型
* @param beanClass Bean类
* @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略
* @return Bean对象
* @since 4.6.3
*/
public T toBean(final Class beanClass, final String prefix) {
final T bean = ConstructorUtil.newInstanceIfPossible(beanClass);
return toBean(bean, prefix);
}
/**
* 将配置文件转换为Bean,支持嵌套Bean
* 支持的表达式:
*
*
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
*
*
* @param Bean类型
* @param bean Bean对象
* @return Bean对象
* @since 4.6.3
*/
public T toBean(final T bean) {
return toBean(bean, null);
}
/**
* 将配置文件转换为Bean,支持嵌套Bean
* 支持的表达式:
*
*
* persion
* persion.name
* persons[3]
* person.friends[5].name
* ['person']['friends'][5]['name']
*
*
* @param Bean类型
* @param bean Bean对象
* @param prefix 公共前缀,不指定前缀传null,当指定前缀后非此前缀的属性被忽略
* @return Bean对象
* @since 4.6.3
*/
public T toBean(final T bean, String prefix) {
prefix = StrUtil.toStringOrEmpty(StrUtil.addSuffixIfNot(prefix, StrUtil.DOT));
String key;
for (final java.util.Map.Entry