Merge remote-tracking branch 'upstream/v5-dev' into v5-dev

This commit is contained in:
lzpeng723 2020-11-30 22:43:53 +08:00
commit eee79cfaad
34 changed files with 1077 additions and 519 deletions

View File

@ -24,6 +24,10 @@
* 【cache 】 增加CacheListenerissue#1257@Github
* 【core 】 TimeInterval支持分组issue#1238@Github
* 【core 】 增加compile包pr#1243@Github
* 【core 】 增加ResourceClassLoader、CharSequenceResource、FileObjectResource
* 【core 】 修改IoUtil.read(Reader)逻辑默认关闭Reader
* 【core 】 ZipUtil增加Zip方法pr#222@Gitee
* 【all 】 增加Hutool.getAllUtils和printAllUtils方法
### Bug修复
* 【cron 】 修复CronTimer可能死循环的问题issue#1224@Github

View File

@ -16,24 +16,51 @@
package cn.hutool;
import cn.hutool.core.lang.ConsoleTable;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import java.util.Set;
/**
* <p>
* Hutool是一个小而全的Java工具类库通过静态方法封装降低相关API的学习成本提高工作效率使Java拥有函数式语言般的优雅让Java语言也可以甜甜的
* </p>
*
*
* <p>
* Hutool中的工具方法来自于每个用户的精雕细琢它涵盖了Java开发底层代码中的方方面面它既是大型项目开发中解决小问题的利器也是小型项目中的效率担当<br>
* </p>
*
* <p>Hutool是项目中util包友好的替代它节省了开发人员对项目中公用类和公用工具方法的封装时间使开发专注于业务同时可以最大限度的避免封装不完善带来的bug</p>
*
* @author Looly
*
* @author Looly
*/
public class Hutool {
public static final String AUTHOR = "Looly";
private Hutool() {
}
/**
* 显示Hutool所有的工具类
*
* @since 5.5.2
*/
public static Set<Class<?>> getAllUtils() {
return ClassUtil.scanPackage("cn.hutool",
(clazz) -> (false == clazz.isInterface()) && StrUtil.endWith(clazz.getSimpleName(), "Util"));
}
/**
* 控制台打印所有工具类
*/
public static void printAllUtils() {
final Set<Class<?>> allUtils = getAllUtils();
final ConsoleTable consoleTable = ConsoleTable.create().addHeader("工具类名", "所在包");
for (Class<?> clazz : allUtils) {
consoleTable.addBody(clazz.getSimpleName(), clazz.getPackage().getName());
}
consoleTable.print();
}
}

View File

@ -0,0 +1,34 @@
package cn.hutool.core.compiler;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
/**
* 编译异常
*
* @author looly
* @since 5.5.2
*/
public class CompilerException extends RuntimeException {
private static final long serialVersionUID = 1L;
public CompilerException(Throwable e) {
super(ExceptionUtil.getMessage(e), e);
}
public CompilerException(String message) {
super(message);
}
public CompilerException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public CompilerException(String message, Throwable throwable) {
super(message, throwable);
}
public CompilerException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}

View File

@ -0,0 +1,69 @@
package cn.hutool.core.compiler;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* 源码编译工具类主要封装{@link JavaCompiler} 相关功能
*
* @author looly
* @since 5.5.2
*/
public class CompilerUtil {
/**
* java 编译器
*/
public static final JavaCompiler SYSTEM_COMPILER = ToolProvider.getSystemJavaCompiler();
/**
* 编译指定的源码文件
*
* @param sourceFiles 源码文件路径
* @return 0表示成功否则其他
*/
public static boolean compile(String... sourceFiles) {
return 0 == SYSTEM_COMPILER.run(null, null, null, sourceFiles);
}
/**
* 获取{@link StandardJavaFileManager}
*
* @return {@link StandardJavaFileManager}
*/
public static StandardJavaFileManager getFileManager() {
return SYSTEM_COMPILER.getStandardFileManager(null, null, null);
}
/**
* 新建编译任务
*
* @param fileManager {@link JavaFileManager}用于管理已经编译好的文件
* @param diagnosticListener 诊断监听
* @param options 选项例如 -cpXXX等
* @param compilationUnits 编译单元即需要编译的对象
* @return {@link JavaCompiler.CompilationTask}
*/
public static JavaCompiler.CompilationTask getTask(
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<? extends JavaFileObject> compilationUnits) {
return SYSTEM_COMPILER.getTask(null, fileManager, diagnosticListener, options, null, compilationUnits);
}
/**
* 获取{@link JavaSourceCompiler}
*
* @param parent {@link ClassLoader}
* @return {@link JavaSourceCompiler}
* @see JavaSourceCompiler#create(ClassLoader)
*/
public static JavaSourceCompiler getCompiler(ClassLoader parent) {
return JavaSourceCompiler.create(parent);
}
}

View File

@ -0,0 +1,26 @@
package cn.hutool.core.compiler;
import javax.tools.DiagnosticCollector;
import java.util.List;
import java.util.stream.Collectors;
/**
* 诊断工具类
*
* @author looly
* @since 5.5.2
*/
public class DiagnosticUtil {
/**
* 获取{@link DiagnosticCollector}收集到的诊断信息以文本返回
*
* @param collector {@link DiagnosticCollector}
* @return 诊断消息
*/
public static String getMessages(DiagnosticCollector<?> collector) {
final List<?> diagnostics = collector.getDiagnostics();
return diagnostics.stream().map(String::valueOf)
.collect(Collectors.joining(System.lineSeparator()));
}
}

View File

@ -1,7 +1,7 @@
package cn.hutool.core.compiler;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.FileObjectResource;
import cn.hutool.core.lang.ResourceClassLoader;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ObjectUtil;
@ -10,16 +10,16 @@ import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Map;
/**
* Java 字节码文件对象
* Java 字节码文件对象管理器
*
* <p>
* 正常我们使用javac命令编译源码时会将class文件写入到磁盘中但在运行时动态编译类不适合保存在磁盘中
* 我们采取此对象来管理运行时动态编译类生成的字节码
* 我们采取此对象来管理运行时动态编译类生成的字节码
* </p>
*
* @author lzpeng
* @since 5.5.2
@ -29,7 +29,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
/**
* 存储java字节码文件对象映射
*/
private final Map<String, JavaFileObject> javaFileObjectMap = new HashMap<>();
private final Map<String, FileObjectResource> classFileObjectMap = new HashMap<>();
/**
* 加载动态编译生成类的父类加载器
@ -55,28 +55,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
*/
@Override
public ClassLoader getClassLoader(final Location location) {
return new SecureClassLoader(parent) {
/**
* 查找类
* @param name 类名
* @return 类的class对象
* @throws ClassNotFoundException 未找到类异常
*/
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
if (null != javaFileObject) {
try(final InputStream inputStream = javaFileObject.openInputStream()){
final byte[] bytes = IoUtil.readBytes(inputStream);
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
throw new ClassNotFoundException(name);
}
};
return new ResourceClassLoader<>(this.parent, this.classFileObjectMap);
}
/**
@ -86,13 +65,13 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
* @param location 源码位置
* @param className 类名
* @param kind 文件类型
* @param sibling Java源码对象
* @param sibling Java源码对象
* @return Java字节码文件对象
*/
@Override
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind);
javaFileObjectMap.put(className, javaFileObject);
final JavaFileObject javaFileObject = new JavaClassFileObject(className);
this.classFileObjectMap.put(className, new FileObjectResource(javaFileObject));
return javaFileObject;
}

View File

@ -1,22 +1,22 @@
package cn.hutool.core.compiler;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.URLUtil;
import javax.tools.SimpleJavaFileObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
/**
* Java 字节码文件对象
* Java 字节码文件对象用于在内存中暂存class字节码从而可以在ClassLoader中动态加载
*
* @author lzpeng
* @see JavaClassFileManager#getClassLoader(javax.tools.JavaFileManager.Location
* @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
* @since 5.5.2
*/
final class JavaClassFileObject extends SimpleJavaFileObject {
class JavaClassFileObject extends SimpleJavaFileObject {
/**
* 字节码输出流
@ -26,12 +26,11 @@ final class JavaClassFileObject extends SimpleJavaFileObject {
/**
* 构造
*
* @param className 需要编译的类名
* @param kind 需要编译的文件类型
* @param className 编译后的class文件的类名
* @see JavaClassFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
*/
protected JavaClassFileObject(final String className, final Kind kind) {
super(URI.create("string:///" + className.replaceAll("\\.", "/") + kind.extension), kind);
protected JavaClassFileObject(String className) {
super(URLUtil.getStringURI(className.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.CLASS.extension), Kind.CLASS);
this.byteArrayOutputStream = new ByteArrayOutputStream();
}

View File

@ -0,0 +1,75 @@
package cn.hutool.core.compiler;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ZipUtil;
import javax.tools.JavaFileObject;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipFile;
/**
* {@link JavaFileObject} 相关工具类封装
*
* @author lzpeng, looly
* @since 5.5.2
*/
public class JavaFileObjectUtil {
/**
* 获取指定文件下的所有待编译的java文件并以{@link JavaFileObject}形式返回
*
* @param file 文件或目录文件支持.java.jar和.zip文件
* @return 所有待编译的 {@link JavaFileObject}
*/
public static List<JavaFileObject> getJavaFileObjects(File file) {
final List<JavaFileObject> result = new ArrayList<>();
final String fileName = file.getName();
if (isJavaFile(fileName)) {
result.add(new JavaSourceFileObject(file.toURI()));
} else if (isJarOrZipFile(fileName)) {
result.addAll(getJavaFileObjectByZipOrJarFile(file));
}
return result;
}
/**
* 是否是jar zip 文件
*
* @param fileName 文件名
* @return 是否是jar zip 文件
*/
public static boolean isJarOrZipFile(String fileName) {
return FileNameUtil.isType(fileName, "jar", "zip");
}
/**
* 是否是java文件
*
* @param fileName 文件名
* @return 是否是.java文件
*/
public static boolean isJavaFile(String fileName) {
return FileNameUtil.isType(fileName, "java");
}
/**
* 通过zip包或jar包创建Java文件对象
*
* @param file 压缩文件
* @return Java文件对象
*/
private static List<JavaFileObject> getJavaFileObjectByZipOrJarFile(File file) {
final List<JavaFileObject> collection = new ArrayList<>();
final ZipFile zipFile = ZipUtil.toZipFile(file, null);
ZipUtil.read(zipFile, (zipEntry) -> {
final String name = zipEntry.getName();
if (isJavaFile(name)) {
collection.add(new JavaSourceFileObject(name, ZipUtil.getStream(zipFile, zipEntry)));
}
});
return collection;
}
}

View File

@ -1,6 +1,10 @@
package cn.hutool.core.compiler;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.StringResource;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
@ -9,72 +13,65 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Java 源码编译器
* <p>通过此类可以动态编译java源码并加载到ClassLoader从而动态获取加载的类</p>
* <p>JavaSourceCompiler支持加载的源码类型包括</p>
* <ul>
* <li>源码文件</li>
* <li>源码文件源码字符串</li>
* </ul>
*
* <p>使用方法如下</p>
* <pre>
* ClassLoader classLoader = JavaSourceCompiler.create(null)
* .addSource(FileUtil.file("test-compile/b/B.java"))
* .addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
* // 增加编译依赖的类库
* .addLibrary(libFile)
* .compile();
* Class&lt;?&gt; clazz = classLoader.loadClass("c.C");
* </pre>
*
* @author lzpeng
*/
public class JavaSourceCompiler {
/**
* java 编译器
* 待编译的资源支持
*
* <ul>
* <li>源码字符串使用{@link StringResource}</li>
* <li>源码文件源码jar包或源码zip包亦或者文件夹使用{@link FileResource}</li>
* </ul>
* 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
*/
private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
/**
* 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
*/
private final List<File> sourceFileList = new ArrayList<>();
private final List<Resource> sourceList = new ArrayList<>();
/**
* 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
*/
private final List<File> libraryFileList = new ArrayList<>();
/**
* 源码映射 key: 类名 value: 类源码
*/
private final Map<String, String> sourceCodeMap = new LinkedHashMap<>();
/**
* 编译类时使用的父类加载器
*/
private final ClassLoader parentClassLoader;
/**
* 构造
*
* @param parent 父类加载器
*/
private JavaSourceCompiler(ClassLoader parent) {
this.parentClassLoader = parent;
}
/**
* 创建Java源码编译器
*
@ -85,16 +82,41 @@ public class JavaSourceCompiler {
return new JavaSourceCompiler(parent);
}
/**
* 构造
*
* @param parent 父类加载器null则使用默认类加载器
*/
private JavaSourceCompiler(ClassLoader parent) {
this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
}
/**
* 向编译器中加入待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
* 向编译器中加入待编译的资源<br>
* 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
*
* @param resources 待编译的资源支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
* @return Java源码编译器
*/
public JavaSourceCompiler addSource(Resource... resources) {
if (ArrayUtil.isNotEmpty(resources)) {
this.sourceList.addAll(Arrays.asList(resources));
}
return this;
}
/**
* 向编译器中加入待编译的文件<br>
* 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
*
* @param files 待编译的文件 支持 .java, 文件夹, 压缩文件 递归搜索文件夹内的压缩文件和jar包
* @return Java源码编译器
*/
public JavaSourceCompiler addSource(final File... files) {
public JavaSourceCompiler addSource(File... files) {
if (ArrayUtil.isNotEmpty(files)) {
this.sourceFileList.addAll(Arrays.asList(files));
for (File file : files) {
this.sourceList.add(new FileResource(file));
}
}
return this;
}
@ -105,36 +127,36 @@ public class JavaSourceCompiler {
* @param sourceCodeMap 源码Map key: 类名 value 源码
* @return Java源码编译器
*/
public JavaSourceCompiler addSource(final Map<String, String> sourceCodeMap) {
public JavaSourceCompiler addSource(Map<String, String> sourceCodeMap) {
if (MapUtil.isNotEmpty(sourceCodeMap)) {
this.sourceCodeMap.putAll(sourceCodeMap);
sourceCodeMap.forEach(this::addSource);
}
return this;
}
/**
* 加入编译Java源码时所需要的jar包
*
* @param files 编译Java源码时所需要的jar包
* @return Java源码编译器
*/
public JavaSourceCompiler addLibrary(final File... files) {
if (ArrayUtil.isNotEmpty(files)) {
this.libraryFileList.addAll(Arrays.asList(files));
}
return this;
}
/**
* 向编译器中加入待编译的源码Map
* 向编译器中加入待编译的源码
*
* @param className 类名
* @param sourceCode 源码
* @return Java文件编译器
*/
public JavaSourceCompiler addSource(final String className, final String sourceCode) {
public JavaSourceCompiler addSource(String className, String sourceCode) {
if (className != null && sourceCode != null) {
this.sourceCodeMap.put(className, sourceCode);
this.sourceList.add(new StringResource(sourceCode, className));
}
return this;
}
/**
* 加入编译Java源码时所需要的jar包jar包中必须为字节码
*
* @param files 编译Java源码时所需要的jar包
* @return Java源码编译器
*/
public JavaSourceCompiler addLibrary(File... files) {
if (ArrayUtil.isNotEmpty(files)) {
this.libraryFileList.addAll(Arrays.asList(files));
}
return this;
}
@ -145,22 +167,17 @@ public class JavaSourceCompiler {
* @return 类加载器
*/
public ClassLoader compile() {
final ClassLoader parent = ObjectUtil.defaultIfNull(this.parentClassLoader, ClassLoaderUtil.getClassLoader());
// 获得classPath
final List<File> classPath = getClassPath();
final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
final URLClassLoader ucl = URLClassLoader.newInstance(urLs, parent);
if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) {
// 没有需要编译的源码
final URLClassLoader ucl = URLClassLoader.newInstance(urLs, this.parentClassLoader);
if (sourceList.isEmpty()) {
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
return ucl;
}
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
// 创建编译器
final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
final JavaClassFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager());
// classpath
final List<String> options = new ArrayList<>();
@ -172,18 +189,18 @@ public class JavaSourceCompiler {
// 编译文件
final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
options, null, javaFileObjectList);
if (task.call()) {
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
} else {
// 编译失败,收集错误信息
final List<?> diagnostics = diagnosticCollector.getDiagnostics();
final String errorMsg = diagnostics.stream().map(String::valueOf)
.collect(Collectors.joining(System.lineSeparator()));
// CompileException
throw new RuntimeException(errorMsg);
final List<JavaFileObject> javaFileObjectList = getJavaFileObject();
final CompilationTask task = CompilerUtil.getTask(javaFileManager, diagnosticCollector, options, javaFileObjectList);
try {
if (task.call()) {
// 加载编译后的类
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
}
} finally {
IoUtil.close(javaFileManager);
}
//编译失败,收集错误信息
throw new CompilerException(DiagnosticUtil.getMessages(diagnosticCollector));
}
/**
@ -194,7 +211,7 @@ public class JavaSourceCompiler {
private List<File> getClassPath() {
List<File> classPathFileList = new ArrayList<>();
for (File file : libraryFileList) {
List<File> jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile);
List<File> jarOrZipFile = FileUtil.loopFiles(file, (subFile) -> JavaFileObjectUtil.isJarOrZipFile(subFile.getName()));
classPathFileList.addAll(jarOrZipFile);
if (file.isDirectory()) {
classPathFileList.add(file);
@ -208,23 +225,19 @@ public class JavaSourceCompiler {
*
* @return 待编译的Java文件对象
*/
private Iterable<JavaFileObject> getJavaFileObject() {
final Collection<JavaFileObject> collection = new ArrayList<>();
for (File file : sourceFileList) {
// .java 文件
final List<File> javaFileList = FileUtil.loopFiles(file, this::isJavaFile);
for (File javaFile : javaFileList) {
collection.add(getJavaFileObjectByJavaFile(javaFile));
}
// 压缩包
final List<File> jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile);
for (File jarOrZipFile : jarOrZipFileList) {
collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile));
private List<JavaFileObject> getJavaFileObject() {
final List<JavaFileObject> list = new ArrayList<>();
for (Resource resource : this.sourceList) {
if (resource instanceof FileResource) {
final File file = ((FileResource) resource).getFile();
FileUtil.walkFiles(file, (subFile) -> list.addAll(JavaFileObjectUtil.getJavaFileObjects(file)));
} else {
list.add(new JavaSourceFileObject(resource.getName(), resource.getStream()));
}
}
// 源码Map
collection.addAll(getJavaFileObjectByMap(this.sourceCodeMap));
return collection;
return list;
}
/**
@ -252,54 +265,4 @@ public class JavaSourceCompiler {
return new JavaSourceFileObject(file.toURI());
}
/**
* 通过zip包或jar包创建Java文件对象
*
* @param file 压缩文件
* @return Java文件对象
*/
private Collection<JavaFileObject> getJavaFileObjectByZipOrJarFile(final File file) {
final Collection<JavaFileObject> collection = new ArrayList<>();
try {
final ZipFile zipFile = new ZipFile(file);
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
final ZipEntry zipEntry = entries.nextElement();
final String name = zipEntry.getName();
if (name.endsWith(".java")) {
final InputStream inputStream = zipFile.getInputStream(zipEntry);
final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream);
collection.add(fileObject);
}
}
return collection;
} catch (IOException e) {
e.printStackTrace();
}
return Collections.emptyList();
}
/**
* 是否是jar zip 文件
*
* @param file 文件
* @return 是否是jar zip 文件
*/
private boolean isJarOrZipFile(final File file) {
final String fileName = file.getName();
return fileName.endsWith(".jar") || fileName.endsWith(".zip");
}
/**
* 是否是.java文件
*
* @param file 文件
* @return 是否是.java文件
*/
private boolean isJavaFile(final File file) {
final String fileName = file.getName();
return fileName.endsWith(".java");
}
}

View File

@ -1,6 +1,8 @@
package cn.hutool.core.compiler;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.URLUtil;
import javax.tools.SimpleJavaFileObject;
import java.io.BufferedInputStream;
@ -10,7 +12,11 @@ import java.net.URI;
import java.nio.charset.Charset;
/**
* Java 源码文件对象
* Java 源码文件对象支持<br>
* <ol>
* <li>源文件通过文件的uri传入</li>
* <li>代码内容通过流传入</li>
* </ol>
*
* @author lzpeng
* @since 5.5.2
@ -23,7 +29,7 @@ class JavaSourceFileObject extends SimpleJavaFileObject {
private InputStream inputStream;
/**
* 构造
* 构造支持File等路径类型的源码
*
* @param uri 需要编译的文件uri
*/
@ -32,25 +38,24 @@ class JavaSourceFileObject extends SimpleJavaFileObject {
}
/**
* 构造
*
* @param name 需要编译的文件名
* @param inputStream 输入流
*/
protected JavaSourceFileObject(String name, InputStream inputStream) {
this(URI.create("string:///" + name));
this.inputStream = inputStream;
}
/**
* 构造
* 构造支持String类型的源码
*
* @param className 需要编译的类名
* @param code 需要编译的类源码
*/
protected JavaSourceFileObject(String className, String code, Charset charset) {
this(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension));
this.inputStream = IoUtil.toStream(code, charset);
this(className, IoUtil.toStream(code, charset));
}
/**
* 构造支持流中读取源码例如zip或网络等
*
* @param name 需要编译的文件名
* @param inputStream 输入流
*/
protected JavaSourceFileObject(String name, InputStream inputStream) {
this(URLUtil.getStringURI(name.replace(CharUtil.DOT, CharUtil.SLASH) + Kind.SOURCE.extension));
this.inputStream = inputStream;
}
/**

View File

@ -1,6 +1,7 @@
package cn.hutool.core.io;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.file.FileCopier;
import cn.hutool.core.io.file.FileMode;
import cn.hutool.core.io.file.FileNameUtil;
@ -50,6 +51,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
@ -64,11 +66,11 @@ public class FileUtil extends PathUtil {
/**
* Class文件扩展名
*/
public static final String CLASS_EXT = ".class";
public static final String CLASS_EXT = FileNameUtil.EXT_CLASS;
/**
* Jar文件扩展名
*/
public static final String JAR_FILE_EXT = ".jar";
public static final String JAR_FILE_EXT = FileNameUtil.EXT_JAR;
/**
* 在Jar中的路径jar的扩展名形式
*/
@ -171,25 +173,37 @@ public class FileUtil extends PathUtil {
* @return 文件列表
*/
public static List<File> loopFiles(File file, FileFilter fileFilter) {
final List<File> fileList = new ArrayList<>();
if (null == file || false == file.exists()) {
return fileList;
return ListUtil.empty();
}
final List<File> fileList = new ArrayList<>();
walkFiles(file, fileList::add);
return fileList;
}
/**
* 递归遍历目录并处理目录下的文件可以处理目录或文件
* <ul>
* <li>非目录则直接调用{@link Consumer}处理</li>
* <li>目录则递归调用此方法处理</li>
* </ul>
*
* @param file 文件或目录文件直接处理
* @param consumer 文件处理器只会处理文件
* @since 5.5.2
*/
public static void walkFiles(File file, Consumer<File> consumer) {
if (file.isDirectory()) {
final File[] subFiles = file.listFiles();
if (ArrayUtil.isNotEmpty(subFiles)) {
for (File tmp : subFiles) {
fileList.addAll(loopFiles(tmp, fileFilter));
walkFiles(tmp, consumer);
}
}
} else {
if (null == fileFilter || fileFilter.accept(file)) {
fileList.add(file);
}
consumer.accept(file);
}
return fileList;
}
/**
@ -968,7 +982,7 @@ public class FileUtil extends PathUtil {
* 移动文件或者目录
*
* @param src 源文件或者目录
* @param target 目标文件或者目录
* @param target 目标文件或者目录
* @param isOverride 是否覆盖目标只有目标为文件才覆盖
* @throws IORuntimeException IO异常
* @see PathUtil#move(Path, Path, boolean)
@ -2900,6 +2914,7 @@ public class FileUtil extends PathUtil {
/**
* 将流的内容写入文件<br>
* 此方法会自动关闭输入流
*
* @param dest 目标文件
* @param in 输入流
@ -2912,6 +2927,7 @@ public class FileUtil extends PathUtil {
/**
* 将流的内容写入文件<br>
* 此方法会自动关闭输入流
*
* @param in 输入流
* @param fullFilePath 文件绝对路径
@ -2923,7 +2939,7 @@ public class FileUtil extends PathUtil {
}
/**
* 将文件写入流中
* 将文件写入流中此方法不会概念比输出流
*
* @param file 文件
* @param out

View File

@ -500,13 +500,25 @@ public class IoUtil {
}
/**
* 从Reader中读取String读取完毕后并不关闭Reader
* 从Reader中读取String读取完毕后关闭Reader
*
* @param reader Reader
* @return String
* @throws IORuntimeException IO异常
*/
public static String read(Reader reader) throws IORuntimeException {
return read(reader, true);
}
/**
* {@link Reader}中读取String
*
* @param reader {@link Reader}
* @param isClose 是否关闭{@link Reader}
* @return String
* @throws IORuntimeException IO异常
*/
public static String read(Reader reader, boolean isClose) throws IORuntimeException {
final StringBuilder builder = StrUtil.builder();
final CharBuffer buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
try {
@ -515,6 +527,10 @@ public class IoUtil {
}
} catch (IOException e) {
throw new IORuntimeException(e);
} finally{
if(isClose){
IoUtil.close(reader);
}
}
return builder.toString();
}

View File

@ -15,6 +15,19 @@ import java.util.regex.Pattern;
*/
public class FileNameUtil {
/**
* .java文件扩展名
*/
public static final String EXT_JAVA = ".java";
/**
* .class文件扩展名
*/
public static final String EXT_CLASS = ".class";
/**
* .jar文件扩展名
*/
public static final String EXT_JAR = ".jar";
/**
* 类Unix路径分隔符
*/
@ -232,5 +245,17 @@ public class FileNameUtil {
public static boolean containsInvalid(String fileName) {
return (false == StrUtil.isBlank(fileName)) && ReUtil.contains(FILE_NAME_INVALID_PATTERN_WIN, fileName);
}
/**
* 根据文件名检查文件类型忽略大小写
*
* @param fileName 文件名例如hutool.png
* @param extNames 被检查的扩展名数组同一文件类型可能有多种扩展名扩展名不带.
* @return 是否是指定扩展名的类型
* @since 5.5.2
*/
public static boolean isType(String fileName, String... extNames) {
return StrUtil.equalsAnyIgnoreCase(extName(fileName), extNames);
}
// -------------------------------------------------------------------------------------------- name end
}

View File

@ -31,7 +31,7 @@ public class FileReader extends FileWrapper {
* 创建 FileReader
* @param file 文件
* @param charset 编码使用 {@link CharsetUtil}
* @return {@link FileReader}
* @return FileReader
*/
public static FileReader create(File file, Charset charset){
return new FileReader(file, charset);
@ -40,7 +40,7 @@ public class FileReader extends FileWrapper {
/**
* 创建 FileReader, 编码{@link FileWrapper#DEFAULT_CHARSET}
* @param file 文件
* @return {@link FileReader}
* @return FileReader
*/
public static FileReader create(File file){
return new FileReader(file);
@ -244,19 +244,36 @@ public class FileReader extends FileWrapper {
throw new IORuntimeException(e);
}
}
/**
* 将文件写入流中
*
* 将文件写入流中此方法不会关闭比输出流
*
* @param out
* @return 写出的流byte数
* @throws IORuntimeException IO异常
*/
public long writeToStream(OutputStream out) throws IORuntimeException {
return writeToStream(out, false);
}
/**
* 将文件写入流中
*
* @param out
* @param isCloseOut 是否关闭输出流
* @return 写出的流byte数
* @throws IORuntimeException IO异常
* @since 5.5.2
*/
public long writeToStream(OutputStream out, boolean isCloseOut) throws IORuntimeException {
try (FileInputStream in = new FileInputStream(this.file)){
return IoUtil.copy(in, out);
}catch (IOException e) {
throw new IORuntimeException(e);
} finally{
if(isCloseOut){
IoUtil.close(out);
}
}
}

View File

@ -1,5 +1,12 @@
package cn.hutool.core.io.file;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
@ -13,13 +20,6 @@ import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
/**
* 文件写入器
* @author Looly
@ -32,7 +32,7 @@ public class FileWriter extends FileWrapper{
* 创建 FileWriter
* @param file 文件
* @param charset 编码使用 {@link CharsetUtil}
* @return {@link FileWriter}
* @return FileWriter
*/
public static FileWriter create(File file, Charset charset){
return new FileWriter(file, charset);
@ -41,7 +41,7 @@ public class FileWriter extends FileWrapper{
/**
* 创建 FileWriter, 编码{@link FileWrapper#DEFAULT_CHARSET}
* @param file 文件
* @return {@link FileWriter}
* @return FileWriter
*/
public static FileWriter create(File file){
return new FileWriter(file);
@ -302,13 +302,26 @@ public class FileWriter extends FileWrapper{
/**
* 将流的内容写入文件<br>
* 此方法会关闭输入流
*
* 此方法自动关闭输入流
*
* @param in 输入流不关闭
* @return dest
* @throws IORuntimeException IO异常
*/
public File writeFromStream(InputStream in) throws IORuntimeException {
return writeFromStream(in, true);
}
/**
* 将流的内容写入文件
*
* @param in 输入流不关闭
* @param isCloseIn 是否关闭输入流
* @return dest
* @throws IORuntimeException IO异常
* @since 5.5.2
*/
public File writeFromStream(InputStream in, boolean isCloseIn) throws IORuntimeException {
FileOutputStream out = null;
try {
out = new FileOutputStream(FileUtil.touch(file));
@ -317,6 +330,9 @@ public class FileWriter extends FileWrapper{
throw new IORuntimeException(e);
} finally {
IoUtil.close(out);
if(isCloseIn){
IoUtil.close(in);
}
}
return file;
}

View File

@ -91,6 +91,18 @@ public class PathUtil {
return fileList;
}
/**
* 遍历指定path下的文件并做处理
*
* @param start 起始路径必须为目录
* @param visitor {@link FileVisitor} 接口用于自定义在访问文件时访问目录前后等节点做的操作
* @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor)
* @since 5.5.2
*/
public static void walkFiles(Path start, FileVisitor<? super Path> visitor) {
walkFiles(start, -1, visitor);
}
/**
* 遍历指定path下的文件并做处理
*

View File

@ -3,11 +3,9 @@ package cn.hutool.core.io.resource;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.Charset;
@ -59,11 +57,6 @@ public class BytesResource implements Resource, Serializable {
return new ByteArrayInputStream(this.bytes);
}
@Override
public BufferedReader getReader(Charset charset) {
return new BufferedReader(new StringReader(readStr(charset)));
}
@Override
public String readStr(Charset charset) throws IORuntimeException {
return StrUtil.str(this.bytes, charset);

View File

@ -0,0 +1,90 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.Charset;
/**
* {@link CharSequence}资源字符串做为资源
*
* @author looly
* @since 5.5.2
*/
public class CharSequenceResource implements Resource, Serializable {
private static final long serialVersionUID = 1L;
private final CharSequence data;
private final CharSequence name;
private final Charset charset;
/**
* 构造使用UTF8编码
*
* @param data 资源数据
*/
public CharSequenceResource(CharSequence data) {
this(data, null);
}
/**
* 构造使用UTF8编码
*
* @param data 资源数据
* @param name 资源名称
*/
public CharSequenceResource(CharSequence data, String name) {
this(data, name, CharsetUtil.CHARSET_UTF_8);
}
/**
* 构造
*
* @param data 资源数据
* @param name 资源名称
* @param charset 编码
*/
public CharSequenceResource(CharSequence data, CharSequence name, Charset charset) {
this.data = data;
this.name = name;
this.charset = charset;
}
@Override
public String getName() {
return this.name.toString();
}
@Override
public URL getUrl() {
return null;
}
@Override
public InputStream getStream() {
return new ByteArrayInputStream(readBytes());
}
@Override
public BufferedReader getReader(Charset charset) {
return IoUtil.getReader(new StringReader(this.data.toString()));
}
@Override
public String readStr(Charset charset) throws IORuntimeException {
return this.data.toString();
}
@Override
public byte[] readBytes() throws IORuntimeException {
return this.data.toString().getBytes(this.charset);
}
}

View File

@ -0,0 +1,73 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import javax.tools.FileObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
/**
* {@link FileObject} 资源包装
*
* @author looly
* @since 5.5.2
*/
public class FileObjectResource implements Resource {
private final FileObject fileObject;
/**
* 构造
*
* @param fileObject {@link FileObject}
*/
public FileObjectResource(FileObject fileObject) {
this.fileObject = fileObject;
}
/**
* 获取原始的{@link FileObject}
*
* @return {@link FileObject}
*/
public FileObject getFileObject() {
return this.fileObject;
}
@Override
public String getName() {
return this.fileObject.getName();
}
@Override
public URL getUrl() {
try {
return this.fileObject.toUri().toURL();
} catch (MalformedURLException e) {
return null;
}
}
@Override
public InputStream getStream() {
try {
return this.fileObject.openInputStream();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
@Override
public BufferedReader getReader(Charset charset) {
try {
return IoUtil.getReader(this.fileObject.openReader(false));
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -1,21 +1,24 @@
package cn.hutool.core.io.resource;
import java.io.File;
import java.nio.file.Path;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.nio.file.Path;
/**
* 文件资源访问对象
* 文件资源访问对象支持{@link Path} {@link File} 访问
*
* @author looly
*
*/
public class FileResource extends UrlResource {
public class FileResource implements Resource, Serializable {
private static final long serialVersionUID = 1L;
private final File file;
// ----------------------------------------------------------------------- Constructor start
/**
* 构造
@ -43,7 +46,7 @@ public class FileResource extends UrlResource {
* @param fileName 文件名如果为null获取文件本身的文件名
*/
public FileResource(File file, String fileName) {
super(URLUtil.getURL(file), StrUtil.isBlank(fileName) ? file.getName() : fileName);
this.file = file;
}
/**
@ -56,4 +59,36 @@ public class FileResource extends UrlResource {
}
// ----------------------------------------------------------------------- Constructor end
@Override
public String getName() {
return this.file.getName();
}
@Override
public URL getUrl(){
return URLUtil.getURL(this.file);
}
@Override
public InputStream getStream() throws NoResourceException {
return FileUtil.getInputStream(this.file);
}
/**
* 获取文件
*
* @return 文件
*/
public File getFile() {
return this.file;
}
/**
* 返回路径
* @return 返回URL路径
*/
@Override
public String toString() {
return (null == this.file) ? "null" : this.file.toString();
}
}

View File

@ -1,13 +1,8 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.nio.charset.Charset;
/**
* 基于{@link InputStream}的资源获取器<br>
@ -56,32 +51,4 @@ public class InputStreamResource implements Resource, Serializable {
public InputStream getStream() {
return this.in;
}
@Override
public BufferedReader getReader(Charset charset) {
return IoUtil.getReader(this.in, charset);
}
@Override
public String readStr(Charset charset) throws IORuntimeException {
BufferedReader reader = null;
try {
reader = getReader(charset);
return IoUtil.read(reader);
} finally {
IoUtil.close(reader);
}
}
@Override
public byte[] readBytes() throws IORuntimeException {
InputStream in = null;
try {
in = getStream();
return IoUtil.readBytes(in);
} finally {
IoUtil.close(in);
}
}
}

View File

@ -13,80 +13,102 @@ import java.nio.charset.Charset;
/**
* 资源接口定义<br>
* 资源可以是文件URLClassPath中的文件亦或者jar包中的文件
*
* <p>资源是数据表示的统称我们可以将任意的数据封装为一个资源然后读取其内容</p>
* <p>资源可以是文件URLClassPath中的文件亦或者jar(zip)包中的文件</p>
* <p>
* 提供资源接口的意义在于我们可以使用一个方法接收任意类型的数据从而处理数据
* 无需专门针对FileInputStream等写多个重载方法同时也为更好的扩展提供了可能
* </p>
* <p>使用非常简单假设我们需要从classpath中读取一个xml我们不用关心这个文件在目录中还是在jar中</p>
* <pre>
* Resource resource = new ClassPathResource("test.xml");
* String xmlStr = resource.readUtf8Str();
* </pre>
* <p>同样我们可以自己实现Resource接口按照业务需要从任意位置读取数据比如从数据库中</p>
*
* @author looly
* @since 3.2.1
*/
public interface Resource {
/**
* 获取资源名例如文件资源的资源名为文件名
*
* @return 资源名
* @since 4.0.13
*/
String getName();
/**
* 获得解析后的{@link URL}
* 获得解析后的{@link URL}无对应URL的返回{@code null}
*
* @return 解析后的{@link URL}
*/
URL getUrl();
/**
* 获得 {@link InputStream}
*
* @return {@link InputStream}
*/
InputStream getStream();
/**
* 将资源内容写出到流不关闭输出流但是关闭资源流
*
* @param out 输出流
* @throws IORuntimeException IO异常
* @since 5.3.5
*/
default void writeTo(OutputStream out) throws IORuntimeException{
default void writeTo(OutputStream out) throws IORuntimeException {
try (InputStream in = getStream()) {
IoUtil.copy(in, out);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 获得Reader
*
* @param charset 编码
* @return {@link BufferedReader}
*/
BufferedReader getReader(Charset charset);
default BufferedReader getReader(Charset charset) {
return IoUtil.getReader(getStream(), charset);
}
/**
* 读取资源内容读取完毕后会关闭流<br>
* 关闭流并不影响下一次读取
*
*
* @param charset 编码
* @return 读取资源内容
* @throws IORuntimeException 包装{@link IOException}
*/
String readStr(Charset charset) throws IORuntimeException;
default String readStr(Charset charset) throws IORuntimeException {
return IoUtil.read(getReader(charset));
}
/**
* 读取资源内容读取完毕后会关闭流<br>
* 关闭流并不影响下一次读取
*
*
* @return 读取资源内容
* @throws IORuntimeException 包装IOException
*/
default String readUtf8Str() throws IORuntimeException{
default String readUtf8Str() throws IORuntimeException {
return readStr(CharsetUtil.CHARSET_UTF_8);
}
/**
* 读取资源内容读取完毕后会关闭流<br>
* 关闭流并不影响下一次读取
*
*
* @return 读取资源内容
* @throws IORuntimeException 包装IOException
*/
byte[] readBytes() throws IORuntimeException;
default byte[] readBytes() throws IORuntimeException {
return IoUtil.readBytes(getStream());
}
}

View File

@ -1,15 +1,7 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.Charset;
/**
@ -17,13 +9,11 @@ import java.nio.charset.Charset;
*
* @author looly
* @since 4.1.0
* @see CharSequenceResource
*/
public class StringResource implements Resource, Serializable {
public class StringResource extends CharSequenceResource {
private static final long serialVersionUID = 1L;
private final String data;
private final String name;
private final Charset charset;
/**
* 构造使用UTF8编码
@ -31,7 +21,7 @@ public class StringResource implements Resource, Serializable {
* @param data 资源数据
*/
public StringResource(String data) {
this(data, null);
super(data, null);
}
/**
@ -41,7 +31,7 @@ public class StringResource implements Resource, Serializable {
* @param name 资源名称
*/
public StringResource(String data, String name) {
this(data, name, CharsetUtil.CHARSET_UTF_8);
super(data, name, CharsetUtil.CHARSET_UTF_8);
}
/**
@ -52,39 +42,6 @@ public class StringResource implements Resource, Serializable {
* @param charset 编码
*/
public StringResource(String data, String name, Charset charset) {
this.data = data;
this.name = name;
this.charset = charset;
super(data, name, charset);
}
@Override
public String getName() {
return this.name;
}
@Override
public URL getUrl() {
return null;
}
@Override
public InputStream getStream() {
return new ByteArrayInputStream(readBytes());
}
@Override
public BufferedReader getReader(Charset charset) {
return IoUtil.getReader(new StringReader(this.data));
}
@Override
public String readStr(Charset charset) throws IORuntimeException {
return this.data;
}
@Override
public byte[] readBytes() throws IORuntimeException {
return this.data.getBytes(this.charset);
}
}

View File

@ -1,17 +1,13 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.nio.charset.Charset;
/**
* URL资源访问类
@ -72,40 +68,6 @@ public class UrlResource implements Resource, Serializable{
return URLUtil.getStream(url);
}
/**
* 获得Reader
* @param charset 编码
* @return {@link BufferedReader}
* @since 3.0.1
*/
@Override
public BufferedReader getReader(Charset charset){
return URLUtil.getReader(this.url, charset);
}
//------------------------------------------------------------------------------- read
@Override
public String readStr(Charset charset) throws IORuntimeException{
BufferedReader reader = null;
try {
reader = getReader(charset);
return IoUtil.read(reader);
} finally {
IoUtil.close(reader);
}
}
@Override
public byte[] readBytes() throws IORuntimeException{
InputStream in = null;
try {
in = getStream();
return IoUtil.readBytes(in);
} finally {
IoUtil.close(in);
}
}
/**
* 获得File
* @return {@link File}

View File

@ -59,7 +59,7 @@ public class JarClassLoader extends URLClassLoader {
method.setAccessible(true);
final List<File> jars = loopJar(jarFile);
for (File jar : jars) {
ReflectUtil.invoke(loader, method, new Object[]{jar.toURI().toURL()});
ReflectUtil.invoke(loader, method, jar.toURI().toURL());
}
}
} catch (IOException e) {

View File

@ -0,0 +1,52 @@
package cn.hutool.core.lang;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ObjectUtil;
import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Map;
/**
* 资源类加载器可以加载任意类型的资源类
*
* @param <T> {@link Resource}接口实现类
* @author looly
* @since 5.5.2
*/
public class ResourceClassLoader<T extends Resource> extends SecureClassLoader {
private final Map<String, T> resourceMap;
/**
* 构造
*
* @param parentClassLoader 父类加载器null表示默认当前上下文加载器
* @param resourceMap 资源map
*/
public ResourceClassLoader(ClassLoader parentClassLoader, Map<String, T> resourceMap) {
super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader()));
this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>());
}
/**
* 增加需要加载的类资源
* @param resource 资源可以是文件流或者字符串
* @return this
*/
public ResourceClassLoader<T> addResource(T resource){
this.resourceMap.put(resource.getName(), resource);
return this;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
final Resource resource = resourceMap.get(name);
if (null != resource) {
final byte[] bytes = resource.readBytes();
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
}

View File

@ -134,6 +134,18 @@ public class URLUtil {
}
}
/**
* 获取string协议的URL类似于string:///xxxxx
*
* @param content 正文
* @return URL
* @since 5.5.2
*/
public static URI getStringURI(CharSequence content){
final String contentStr = StrUtil.addPrefixIfNot(content, "string:///");
return URI.create(contentStr);
}
/**
* 将URL字符串转换为URL对象并做必要验证
*

View File

@ -5,6 +5,7 @@ import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.Resource;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@ -19,6 +20,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.function.Consumer;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
@ -44,6 +46,37 @@ public class ZipUtil {
*/
private static final Charset DEFAULT_CHARSET = CharsetUtil.defaultCharset();
/**
* 将Zip文件转换为{@link ZipFile}
*
* @param file zip文件
* @param charset 解析zip文件的编码null表示{@link CharsetUtil#CHARSET_UTF_8}
* @return {@link ZipFile}
*/
public static ZipFile toZipFile(File file, Charset charset) {
try {
return new ZipFile(file, ObjectUtil.defaultIfNull(charset, CharsetUtil.CHARSET_UTF_8));
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 获取指定{@link ZipEntry}的流用于读取这个entry的内容
*
* @param zipFile {@link ZipFile}
* @param zipEntry {@link ZipEntry}
* @return
* @since 5.5.2
*/
public static InputStream getStream(ZipFile zipFile, ZipEntry zipEntry) {
try {
return zipFile.getInputStream(zipEntry);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 打包到当前目录使用默认编码UTF-8
*
@ -190,7 +223,7 @@ public class ZipUtil {
/**
* 对文件或文件目录进行压缩
*
* @param out 生成的Zip到的目标流包括文件名注意zipPath不能是srcPath路径下的子文件夹
* @param out 生成的Zip到的目标流包括文件名注意zipPath不能是srcPath路径下的子文件夹
* @param charset 编码
* @param withSrcDir 是否包含被打包目录只针对压缩目录有效若为false则只压缩目录下的文件或目录为true则将本目录也压缩
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩
@ -205,16 +238,16 @@ public class ZipUtil {
/**
* 对文件或文件目录进行压缩
*
* @param zipOutputStream 生成的Zip到的目标流不关闭此流
* @param withSrcDir 是否包含被打包目录只针对压缩目录有效若为false则只压缩目录下的文件或目录为true则将本目录也压缩
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩
* @param srcFiles 要压缩的源文件或目录如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @param zipOutputStream 生成的Zip到的目标流不关闭此流
* @param withSrcDir 是否包含被打包目录只针对压缩目录有效若为false则只压缩目录下的文件或目录为true则将本目录也压缩
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩
* @param srcFiles 要压缩的源文件或目录如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @throws IORuntimeException IO异常
* @since 5.1.1
*/
public static void zip(ZipOutputStream zipOutputStream, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException {
String srcRootDir;
try{
try {
for (File srcFile : srcFiles) {
if (null == srcFile) {
continue;
@ -300,7 +333,7 @@ public class ZipUtil {
*
* @param zipFile 生成的Zip文件包括文件名注意zipPath不能是srcPath路径下的子文件夹
* @param paths 流数据在压缩文件中的路径或文件名
* @param ins 要压缩的源
* @param ins 要压缩的源添加完成后自动关闭流
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.0.9
@ -322,18 +355,72 @@ public class ZipUtil {
* @since 3.0.9
*/
public static File zip(File zipFile, String[] paths, InputStream[] ins, Charset charset) throws UtilException {
ZipOutputStream out = null;
try {
out = getZipOutputStream(zipFile, charset);
zip(out, paths, ins);
} finally {
IoUtil.close(out);
}
return zipFile;
}
/**
* 将文件流压缩到目标流中
*
* @param out 目标流压缩完成自动关闭
* @param paths 流数据在压缩文件中的路径或文件名
* @param ins 要压缩的源添加完成后自动关闭流
* @since 5.5.2
*/
public static void zip(OutputStream out, String[] paths, InputStream[] ins) {
ZipOutputStream zipOutputStream = null;
try {
zipOutputStream = getZipOutputStream(out, DEFAULT_CHARSET);
zip(zipOutputStream, paths, ins);
} finally {
IoUtil.close(zipOutputStream);
}
}
/**
* 将文件流压缩到目标流中
*
* @param zipOutputStream 目标流压缩完成不关闭
* @param paths 流数据在压缩文件中的路径或文件名
* @param ins 要压缩的源添加完成后自动关闭流
* @throws IORuntimeException IO异常
* @since 5.5.2
*/
public static void zip(ZipOutputStream zipOutputStream, String[] paths, InputStream[] ins) throws IORuntimeException {
if (ArrayUtil.isEmpty(paths) || ArrayUtil.isEmpty(ins)) {
throw new IllegalArgumentException("Paths or ins is empty !");
}
if (paths.length != ins.length) {
throw new IllegalArgumentException("Paths length is not equals to ins length !");
}
for (int i = 0; i < paths.length; i++) {
add(ins[i], paths[i], zipOutputStream);
}
}
/**
* 对流中的数据加入到压缩文件<br>
* 路径列表和流列表长度必须一致
*
* @param zipFile 生成的Zip文件包括文件名注意zipPath不能是srcPath路径下的子文件夹
* @param charset 编码
* @param resources 需要压缩的资源资源的路径为{@link Resource#getName()}
* @return 压缩文件
* @throws UtilException IO异常
* @since 5.5.2
*/
public static File zip(File zipFile, Charset charset, Resource... resources) throws UtilException {
ZipOutputStream out = null;
try {
out = getZipOutputStream(zipFile, charset);
for (int i = 0; i < paths.length; i++) {
addFile(ins[i], paths[i], out);
for (Resource resource : resources) {
add(resource.getStream(), resource.getName(), out);
}
} finally {
IoUtil.close(out);
@ -437,17 +524,10 @@ public class ZipUtil {
* @param outFile 解压到的目录
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File unzip(File zipFile, File outFile, Charset charset) throws UtilException {
ZipFile zip;
try {
zip = new ZipFile(zipFile, charset);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return unzip(zip, outFile);
public static File unzip(File zipFile, File outFile, Charset charset) {
return unzip(toZipFile(zipFile, charset), outFile);
}
/**
@ -460,66 +540,71 @@ public class ZipUtil {
* @since 4.5.8
*/
public static File unzip(ZipFile zipFile, File outFile) throws IORuntimeException {
if(outFile.exists() && outFile.isFile()){
if (outFile.exists() && outFile.isFile()) {
throw new UtilException("Target path [{}] exist!", outFile.getAbsolutePath());
}
try {
final Enumeration<? extends ZipEntry> em = zipFile.entries();
ZipEntry zipEntry;
File outItemFile;
while (em.hasMoreElements()) {
zipEntry = em.nextElement();
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 创建对应目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 写出文件
write(zipFile, zipEntry, outItemFile);
}
read(zipFile, (zipEntry) -> {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 创建对应目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 写出文件
write(zipFile, zipEntry, outItemFile);
}
} finally {
IoUtil.close(zipFile);
}
});
return outFile;
}
/**
* 获取压缩包中的指定文件流
*
* @param zipFile 压缩文件
* @param path 需要提取文件的文件名或路径
* @param path 需要提取文件的文件名或路径
* @return 压缩文件流如果未找到返回{@code null}
* @since 5.5.2
*/
public static InputStream get(File zipFile, Charset charset, String path){
try {
return get(new ZipFile(zipFile, charset), path);
} catch (IOException e) {
throw new IORuntimeException(e);
}
public static InputStream get(File zipFile, Charset charset, String path) {
return get(toZipFile(zipFile, charset), path);
}
/**
* 获取压缩包中的指定文件流
*
* @param zipFile 压缩文件
* @param path 需要提取文件的文件名或路径
* @param path 需要提取文件的文件名或路径
* @return 压缩文件流如果未找到返回{@code null}
* @since 5.5.2
*/
public static InputStream get(ZipFile zipFile, String path){
public static InputStream get(ZipFile zipFile, String path) {
final ZipEntry entry = zipFile.getEntry(path);
if(null != entry){
try {
return zipFile.getInputStream(entry);
} catch (IOException e) {
throw new IORuntimeException(e);
}
if (null != entry) {
return getStream(zipFile, entry);
}
return null;
}
/**
* 读取并处理Zip文件中的每一个{@link ZipEntry}
*
* @param zipFile Zip文件
* @param consumer {@link ZipEntry}处理器
* @since 5.5.2
*/
public static void read(ZipFile zipFile, Consumer<ZipEntry> consumer) {
try {
final Enumeration<? extends ZipEntry> em = zipFile.entries();
while (em.hasMoreElements()) {
consumer.accept(em.nextElement());
}
} finally {
IoUtil.close(zipFile);
}
}
/**
* 解压<br>
* ZIP条目不使用高速缓冲
@ -549,27 +634,40 @@ public class ZipUtil {
* @since 4.5.8
*/
public static File unzip(ZipInputStream zipStream, File outFile) throws UtilException {
read(zipStream, (zipEntry) -> {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 文件
FileUtil.writeFromStream(zipStream, outItemFile);
}
});
return outFile;
}
/**
* 读取并处理Zip流中的每一个{@link ZipEntry}
*
* @param zipStream zip文件流包含编码信息
* @param consumer {@link ZipEntry}处理器
* @since 5.5.2
*/
public static void read(ZipInputStream zipStream, Consumer<ZipEntry> consumer) {
try {
ZipEntry zipEntry;
File outItemFile;
while (null != (zipEntry = zipStream.getNextEntry())) {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 文件
FileUtil.writeFromStream(zipStream, outItemFile);
}
consumer.accept(zipEntry);
}
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(zipStream);
}
return outFile;
}
/**
@ -622,17 +720,15 @@ public class ZipUtil {
public static byte[] unzipFileBytes(File zipFile, Charset charset, String name) {
ZipFile zipFileObj = null;
try {
zipFileObj = new ZipFile(zipFile, charset);
zipFileObj = toZipFile(zipFile, charset);
final Enumeration<ZipEntry> em = (Enumeration<ZipEntry>) zipFileObj.entries();
ZipEntry zipEntry;
while (em.hasMoreElements()) {
zipEntry = em.nextElement();
if ((false == zipEntry.isDirectory()) && name.equals(zipEntry.getName())) {
return IoUtil.readBytes(zipFileObj.getInputStream(zipEntry));
return IoUtil.readBytes(getStream(zipFileObj, zipEntry));
}
}
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(zipFileObj);
}
@ -946,8 +1042,8 @@ public class ZipUtil {
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
if(out instanceof ZipOutputStream) {
return (ZipOutputStream)out;
if (out instanceof ZipOutputStream) {
return (ZipOutputStream) out;
}
return new ZipOutputStream(out, ObjectUtil.defaultIfNull(charset, DEFAULT_CHARSET));
}
@ -980,7 +1076,7 @@ public class ZipUtil {
zip(childFile, srcRootDir, out, filter);
}
} else {// 如果是文件或其它符号则直接压缩该文件
addFile(file, subPath, out);
add(file, subPath, out);
}
}
@ -990,22 +1086,22 @@ public class ZipUtil {
* @param file 需要压缩的文件
* @param path 在压缩文件中的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
* @throws IORuntimeException IO异常
* @since 4.0.5
*/
private static void addFile(File file, String path, ZipOutputStream out) throws UtilException {
addFile(FileUtil.getInputStream(file), path, out);
private static void add(File file, String path, ZipOutputStream out) throws IORuntimeException {
add(FileUtil.getInputStream(file), path, out);
}
/**
* 添加文件流到压缩包添加后关闭流
*
* @param in 需要压缩的输入流
* @param in 需要压缩的输入流使用完后自动关闭
* @param path 压缩的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
* @throws IORuntimeException IO异常
*/
private static void addFile(InputStream in, String path, ZipOutputStream out) throws UtilException {
private static void add(InputStream in, String path, ZipOutputStream out) throws IORuntimeException {
if (null == in) {
return;
}
@ -1013,7 +1109,7 @@ public class ZipUtil {
out.putNextEntry(new ZipEntry(path));
IoUtil.copy(in, out);
} catch (IOException e) {
throw new UtilException(e);
throw new IORuntimeException(e);
} finally {
IoUtil.close(in);
closeEntry(out);
@ -1058,7 +1154,7 @@ public class ZipUtil {
}
// 压缩文件不能位于被压缩的目录内
if(srcFile.isDirectory() && FileUtil.isSub(srcFile, zipFile.getParentFile())){
if (srcFile.isDirectory() && FileUtil.isSub(srcFile, zipFile.getParentFile())) {
throw new UtilException("Zip file path [{}] must not be the child directory of [{}] !", zipFile.getPath(), srcFile.getPath());
}
}
@ -1086,15 +1182,7 @@ public class ZipUtil {
* @throws IORuntimeException IO异常
*/
private static void write(ZipFile zipFile, ZipEntry zipEntry, File outItemFile) throws IORuntimeException {
InputStream in = null;
try {
in = zipFile.getInputStream(zipEntry);
FileUtil.writeFromStream(in, outItemFile);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(in);
}
FileUtil.writeFromStream(getStream(zipFile, zipEntry), outItemFile);
}
/**

View File

@ -21,6 +21,7 @@ public class JavaSourceCompilerTest {
*/
@Test
public void testCompile() throws ClassNotFoundException {
// 依赖A编译B和C
final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"),
new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"},
new InputStream[]{

View File

@ -1,5 +1,8 @@
package cn.hutool.core.io.resource;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.Assert;
import org.junit.Test;
@ -9,5 +12,25 @@ public class ResourceUtilTest {
public void readXmlTest(){
final String str = ResourceUtil.readUtf8Str("test.xml");
Assert.assertNotNull(str);
Resource resource = new ClassPathResource("test.xml");
final String xmlStr = resource.readUtf8Str();
Assert.assertEquals(str, xmlStr);
}
@Test
public void stringResourceTest(){
final StringResource stringResource = new StringResource("testData", "test");
Assert.assertEquals("test", stringResource.getName());
Assert.assertArrayEquals("testData".getBytes(), stringResource.readBytes());
Assert.assertArrayEquals("testData".getBytes(), IoUtil.readBytes(stringResource.getStream()));
}
@Test
public void fileResourceTest(){
final FileResource resource = new FileResource(FileUtil.file("test.xml"));
Assert.assertEquals("test.xml", resource.getName());
Assert.assertTrue(StrUtil.isNotEmpty(resource.readUtf8Str()));
}
}

View File

@ -2,7 +2,7 @@ package cn.hutool.setting;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.UrlResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
@ -70,17 +70,17 @@ public class SettingLoader {
/**
* 加载设置文件
*
* @param urlResource 配置文件URL
* @param resource 配置文件URL
* @return 加载是否成功
*/
public boolean load(UrlResource urlResource) {
if (urlResource == null) {
public boolean load(Resource resource) {
if (resource == null) {
throw new NullPointerException("Null setting url define!");
}
log.debug("Load setting file [{}]", urlResource);
log.debug("Load setting file [{}]", resource);
InputStream settingStream = null;
try {
settingStream = urlResource.getStream();
settingStream = resource.getStream();
load(settingStream);
} catch (Exception e) {
log.error(e, "Load setting error!");

View File

@ -1,6 +1,6 @@
package cn.hutool.setting;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.resource.NoResourceException;
import cn.hutool.core.util.StrUtil;
@ -27,22 +27,13 @@ public class SettingUtil {
* @return 当前环境下配置文件
*/
public static Setting get(String name) {
Setting setting = SETTING_MAP.get(name);
if (null == setting) {
synchronized (SettingUtil.class) {
setting = SETTING_MAP.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);
SETTING_MAP.put(name, setting);
}
return SETTING_MAP.computeIfAbsent(name, (filePath)->{
final String extName = FileNameUtil.extName(filePath);
if (StrUtil.isEmpty(extName)) {
filePath = filePath + "." + Setting.EXT_NAME;
}
}
return setting;
return new Setting(filePath, true);
});
}
/**

View File

@ -21,7 +21,6 @@ import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingRuntimeException;
import java.io.BufferedReader;
@ -239,7 +238,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
if (null != charset) {
this.charset = charset;
}
this.load(new UrlResource(propertiesUrl));
this.load(propertiesUrl);
}
/**
@ -257,16 +256,26 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
/**
* 初始化配置文件
*
* @param urlResource {@link UrlResource}
*
* @param url {@link URL}
* @since 5.5.2
*/
public void load(Resource urlResource) {
this.propertiesFileUrl = urlResource.getUrl();
public void load(URL url) {
load(new UrlResource(url));
}
/**
* 初始化配置文件
*
* @param resource {@link Resource}
*/
public void load(Resource resource) {
this.propertiesFileUrl = resource.getUrl();
if (null == this.propertiesFileUrl) {
throw new SettingRuntimeException("Can not find properties file: [{}]", urlResource);
throw new SettingRuntimeException("Can not find properties file: [{}]", resource);
}
try (final BufferedReader reader = urlResource.getReader(charset)) {
try (final BufferedReader reader = resource.getReader(charset)) {
super.load(reader);
} catch (IOException e) {
throw new IORuntimeException(e);
@ -277,7 +286,7 @@ public final class Props extends Properties implements BasicTypeGetter<String>,
* 重新加载配置文件
*/
public void load() {
this.load(new UrlResource(this.propertiesFileUrl));
this.load(this.propertiesFileUrl);
}
/**

View File

@ -20,7 +20,6 @@ public class PropsUtil {
* 配置文件缓存
*/
private static final Map<String, Props> propsMap = new ConcurrentHashMap<>();
private static final Object lock = new Object();
/**
* 获取当前环境下的配置文件<br>
@ -30,22 +29,13 @@ public class PropsUtil {
* @return 当前环境下配置文件
*/
public static Props get(String name) {
Props props = propsMap.get(name);
if (null == props) {
synchronized (lock) {
props = propsMap.get(name);
if (null == props) {
String filePath = name;
String extName = FileUtil.extName(filePath);
if (StrUtil.isEmpty(extName)) {
filePath = filePath + "." + Props.EXT_NAME;
}
props = new Props(filePath);
propsMap.put(name, props);
}
return propsMap.computeIfAbsent(name, (filePath)->{
final String extName = FileUtil.extName(filePath);
if (StrUtil.isEmpty(extName)) {
filePath = filePath + "." + Props.EXT_NAME;
}
}
return props;
return new Props(filePath);
});
}
/**
@ -66,4 +56,14 @@ public class PropsUtil {
}
return null;
}
/**
* 获取系统参数例如用户在执行java命令时定义的 -Duse=hutool
*
* @return 系统参数Props
* @since 5.5.2
*/
public static Props getSystemProps(){
return new Props(System.getProperties());
}
}