diff --git a/CHANGELOG.md b/CHANGELOG.md
index c44ecbfa6..c2ddb1051 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,10 @@
* 【cache 】 增加CacheListener(issue#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)
diff --git a/hutool-all/src/main/java/cn/hutool/Hutool.java b/hutool-all/src/main/java/cn/hutool/Hutool.java
index 18fe66962..891b5b499 100644
--- a/hutool-all/src/main/java/cn/hutool/Hutool.java
+++ b/hutool-all/src/main/java/cn/hutool/Hutool.java
@@ -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;
+
/**
*
* Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
*
- *
+ *
*
* Hutool中的工具方法来自于每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
*
*
* Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
- *
- * @author Looly
*
+ * @author Looly
*/
public class Hutool {
-
+
public static final String AUTHOR = "Looly";
-
+
private Hutool() {
}
+
+ /**
+ * 显示Hutool所有的工具类
+ *
+ * @since 5.5.2
+ */
+ public static Set> getAllUtils() {
+ return ClassUtil.scanPackage("cn.hutool",
+ (clazz) -> (false == clazz.isInterface()) && StrUtil.endWith(clazz.getSimpleName(), "Util"));
+ }
+
+ /**
+ * 控制台打印所有工具类
+ */
+ public static void printAllUtils() {
+ final Set> allUtils = getAllUtils();
+ final ConsoleTable consoleTable = ConsoleTable.create().addHeader("工具类名", "所在包");
+ for (Class> clazz : allUtils) {
+ consoleTable.addBody(clazz.getSimpleName(), clazz.getPackage().getName());
+ }
+ consoleTable.print();
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java
new file mode 100644
index 000000000..8cf07e8be
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerException.java
@@ -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);
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java
new file mode 100644
index 000000000..148252b03
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java
@@ -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 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);
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java
new file mode 100644
index 000000000..fe04dba5f
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/DiagnosticUtil.java
@@ -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()));
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java
index 0de53681c..1150f9e5c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java
@@ -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 字节码文件对象管理器
+ *
+ *
* 正常我们使用javac命令编译源码时会将class文件写入到磁盘中,但在运行时动态编译类不适合保存在磁盘中
- * 我们采取此对象来管理运行时动态编译类生成的字节码
+ * 我们采取此对象来管理运行时动态编译类生成的字节码。
+ *
*
* @author lzpeng
* @since 5.5.2
@@ -29,7 +29,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager {
/**
* 存储java字节码文件对象映射
*/
- private final Map javaFileObjectMap = new HashMap<>();
+ private final Map classFileObjectMap = new HashMap<>();
/**
* 加载动态编译生成类的父类加载器
@@ -55,28 +55,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager {
*/
@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 {
* @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;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java
index d707a1c18..b1d6e8792 100644
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileObject.java
@@ -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();
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java
new file mode 100644
index 000000000..9da8ac4e3
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaFileObjectUtil.java
@@ -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 getJavaFileObjects(File file) {
+ final List 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 getJavaFileObjectByZipOrJarFile(File file) {
+ final List 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;
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java
index 9f2650c8e..609bf0423 100644
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java
@@ -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 源码编译器
+ * 通过此类可以动态编译java源码,并加载到ClassLoader,从而动态获取加载的类。
+ * JavaSourceCompiler支持加载的源码类型包括:
+ *
+ * - 源码文件
+ * - 源码文件源码字符串
+ *
+ *
+ * 使用方法如下:
+ *
+ * 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<?> clazz = classLoader.loadClass("c.C");
+ *
*
* @author lzpeng
*/
public class JavaSourceCompiler {
/**
- * java 编译器
+ * 待编译的资源,支持:
+ *
+ *
+ * - 源码字符串,使用{@link StringResource}
+ * - 源码文件、源码jar包或源码zip包,亦或者文件夹,使用{@link FileResource}
+ *
+ * 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
*/
- private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
-
- /**
- * 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
- */
- private final List sourceFileList = new ArrayList<>();
+ private final List sourceList = new ArrayList<>();
/**
* 编译时需要加入classpath中的文件 可以是 压缩文件 文件夹递归搜索文件夹内的zip包和jar包
*/
private final List libraryFileList = new ArrayList<>();
- /**
- * 源码映射 key: 类名 value: 类源码
- */
- private final Map 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包
+ * 向编译器中加入待编译的资源
+ * 支持 .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;
+ }
+
+ /**
+ * 向编译器中加入待编译的文件
+ * 支持 .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 sourceCodeMap) {
+ public JavaSourceCompiler addSource(Map 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 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 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 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 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 getClassPath() {
List classPathFileList = new ArrayList<>();
for (File file : libraryFileList) {
- List jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile);
+ List 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 getJavaFileObject() {
- final Collection collection = new ArrayList<>();
- for (File file : sourceFileList) {
- // .java 文件
- final List javaFileList = FileUtil.loopFiles(file, this::isJavaFile);
- for (File javaFile : javaFileList) {
- collection.add(getJavaFileObjectByJavaFile(javaFile));
- }
- // 压缩包
- final List jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile);
- for (File jarOrZipFile : jarOrZipFileList) {
- collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile));
+ private List getJavaFileObject() {
+ final List 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 getJavaFileObjectByZipOrJarFile(final File file) {
- final Collection 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");
- }
-
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java
index e8cae9da8..db0c0dd8c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java
+++ b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceFileObject.java
@@ -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 源码文件对象,支持:
+ *
+ * - 源文件,通过文件的uri传入
+ * - 代码内容,通过流传入
+ *
*
* @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;
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
index 6583e98d2..49ae0f596 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java
@@ -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 loopFiles(File file, FileFilter fileFilter) {
- final List fileList = new ArrayList<>();
if (null == file || false == file.exists()) {
- return fileList;
+ return ListUtil.empty();
}
+ final List fileList = new ArrayList<>();
+ walkFiles(file, fileList::add);
+ return fileList;
+ }
+
+ /**
+ * 递归遍历目录并处理目录下的文件,可以处理目录或文件:
+ *
+ * - 非目录则直接调用{@link Consumer}处理
+ * - 目录则递归调用此方法处理
+ *
+ *
+ * @param file 文件或目录,文件直接处理
+ * @param consumer 文件处理器,只会处理文件
+ * @since 5.5.2
+ */
+ public static void walkFiles(File file, Consumer 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 {
/**
* 将流的内容写入文件
+ * 此方法会自动关闭输入流
*
* @param dest 目标文件
* @param in 输入流
@@ -2912,6 +2927,7 @@ public class FileUtil extends PathUtil {
/**
* 将流的内容写入文件
+ * 此方法会自动关闭输入流
*
* @param in 输入流
* @param fullFilePath 文件绝对路径
@@ -2923,7 +2939,7 @@ public class FileUtil extends PathUtil {
}
/**
- * 将文件写入流中
+ * 将文件写入流中,此方法不会概念比输出流
*
* @param file 文件
* @param out 流
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
index 134f377fa..82c941f72 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
@@ -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();
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
index 406c066b9..cc874827d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileNameUtil.java
@@ -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
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
index 93808ce89..9c481787c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileReader.java
@@ -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);
+ }
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
index 12438f713..a6aeb40fe 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/FileWriter.java
@@ -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{
/**
* 将流的内容写入文件
- * 此方法不会关闭输入流
- *
+ * 此方法会自动关闭输入流
+ *
* @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;
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
index 50ee10ca9..5b7baaa07 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/file/PathUtil.java
@@ -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下的文件并做处理
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/BytesResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/BytesResource.java
index f3607ba4b..457681d59 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/BytesResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/BytesResource.java
@@ -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);
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/CharSequenceResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/CharSequenceResource.java
new file mode 100644
index 000000000..1b4fcfd59
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/CharSequenceResource.java
@@ -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);
+ }
+
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileObjectResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileObjectResource.java
new file mode 100644
index 000000000..0e435c06b
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileObjectResource.java
@@ -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);
+ }
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
index 0d854b185..2fd77011f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/FileResource.java
@@ -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();
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/InputStreamResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/InputStreamResource.java
index e861329f2..0a4e1bf81 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/InputStreamResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/InputStreamResource.java
@@ -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}的资源获取器
@@ -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);
- }
- }
-
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/Resource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/Resource.java
index 98989a29e..2ec9d844f 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/Resource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/Resource.java
@@ -13,80 +13,102 @@ import java.nio.charset.Charset;
/**
* 资源接口定义
- * 资源可以是文件、URL、ClassPath中的文件亦或者jar包中的文件
- *
+ * 资源是数据表示的统称,我们可以将任意的数据封装为一个资源,然后读取其内容。
+ * 资源可以是文件、URL、ClassPath中的文件亦或者jar(zip)包中的文件。
+ *
+ * 提供资源接口的意义在于,我们可以使用一个方法接收任意类型的数据,从而处理数据,
+ * 无需专门针对File、InputStream等写多个重载方法,同时也为更好的扩展提供了可能。
+ *
+ * 使用非常简单,假设我们需要从classpath中读取一个xml,我们不用关心这个文件在目录中还是在jar中:
+ *
+ * Resource resource = new ClassPathResource("test.xml");
+ * String xmlStr = resource.readUtf8Str();
+ *
+ * 同样,我们可以自己实现Resource接口,按照业务需要从任意位置读取数据,比如从数据库中。
+ *
* @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);
+ }
+
/**
* 读取资源内容,读取完毕后会关闭流
* 关闭流并不影响下一次读取
- *
+ *
* @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));
+ }
+
/**
* 读取资源内容,读取完毕后会关闭流
* 关闭流并不影响下一次读取
- *
+ *
* @return 读取资源内容
* @throws IORuntimeException 包装IOException
*/
- default String readUtf8Str() throws IORuntimeException{
+ default String readUtf8Str() throws IORuntimeException {
return readStr(CharsetUtil.CHARSET_UTF_8);
}
-
+
/**
* 读取资源内容,读取完毕后会关闭流
* 关闭流并不影响下一次读取
- *
+ *
* @return 读取资源内容
* @throws IORuntimeException 包装IOException
*/
- byte[] readBytes() throws IORuntimeException;
+ default byte[] readBytes() throws IORuntimeException {
+ return IoUtil.readBytes(getStream());
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/StringResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/StringResource.java
index 0f45a4972..16158d1a0 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/StringResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/StringResource.java
@@ -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);
- }
-
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
index 995a5923f..96d0293f5 100644
--- a/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/UrlResource.java
@@ -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}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/JarClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/JarClassLoader.java
index 0ae55f932..58d5cb26d 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/JarClassLoader.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/JarClassLoader.java
@@ -59,7 +59,7 @@ public class JarClassLoader extends URLClassLoader {
method.setAccessible(true);
final List 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) {
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java
new file mode 100644
index 000000000..76135cf56
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/ResourceClassLoader.java
@@ -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 {@link Resource}接口实现类
+ * @author looly
+ * @since 5.5.2
+ */
+public class ResourceClassLoader extends SecureClassLoader {
+
+ private final Map resourceMap;
+
+ /**
+ * 构造
+ *
+ * @param parentClassLoader 父类加载器,null表示默认当前上下文加载器
+ * @param resourceMap 资源map
+ */
+ public ResourceClassLoader(ClassLoader parentClassLoader, Map resourceMap) {
+ super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader()));
+ this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>());
+ }
+
+ /**
+ * 增加需要加载的类资源
+ * @param resource 资源,可以是文件、流或者字符串
+ * @return this
+ */
+ public ResourceClassLoader 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);
+ }
+}
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java
index 8a52a090b..4ba026e38 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/URLUtil.java
@@ -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对象,并做必要验证
*
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java
index ccafd1506..dabca85c3 100644
--- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java
@@ -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);
+ }
+ }
+ /**
+ * 对流中的数据加入到压缩文件
+ * 路径列表和流列表长度必须一致
+ *
+ * @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 consumer) {
+ try {
+ final Enumeration extends ZipEntry> em = zipFile.entries();
+ while (em.hasMoreElements()) {
+ consumer.accept(em.nextElement());
+ }
+ } finally {
+ IoUtil.close(zipFile);
+ }
+ }
+
/**
* 解压
* 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 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 em = (Enumeration) 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);
}
/**
diff --git a/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java b/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java
index 3e24514d7..5131e7bdb 100644
--- a/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/compiler/JavaSourceCompilerTest.java
@@ -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[]{
diff --git a/hutool-core/src/test/java/cn/hutool/core/io/resource/ResourceUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/io/resource/ResourceUtilTest.java
index 36b2d72bc..1e4bedfc1 100644
--- a/hutool-core/src/test/java/cn/hutool/core/io/resource/ResourceUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/io/resource/ResourceUtilTest.java
@@ -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()));
}
}
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
index 401e0a878..adc1fd9fe 100644
--- a/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
+++ b/hutool-setting/src/main/java/cn/hutool/setting/SettingLoader.java
@@ -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!");
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java b/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java
index 44c7ba089..776ed8933 100644
--- a/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java
+++ b/hutool-setting/src/main/java/cn/hutool/setting/SettingUtil.java
@@ -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);
+ });
}
/**
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java
index 5c5c08513..afd72ce39 100644
--- a/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java
+++ b/hutool-setting/src/main/java/cn/hutool/setting/dialect/Props.java
@@ -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,
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,
/**
* 初始化配置文件
- *
- * @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,
* 重新加载配置文件
*/
public void load() {
- this.load(new UrlResource(this.propertiesFileUrl));
+ this.load(this.propertiesFileUrl);
}
/**
diff --git a/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java b/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java
index 31cfed5ec..a8a121c63 100644
--- a/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java
+++ b/hutool-setting/src/main/java/cn/hutool/setting/dialect/PropsUtil.java
@@ -20,7 +20,6 @@ public class PropsUtil {
* 配置文件缓存
*/
private static final Map propsMap = new ConcurrentHashMap<>();
- private static final Object lock = new Object();
/**
* 获取当前环境下的配置文件
@@ -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());
+ }
}