diff --git a/CHANGELOG.md b/CHANGELOG.md index 78aa0974d..e6dca943a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * 【core 】 增加compile包(pr#1243@Github) * 【core 】 增加ResourceClassLoader、CharSequenceResource、FileObjectResource * 【core 】 修改IoUtil.read(Reader)逻辑默认关闭Reader +* 【core 】 ZipUtil增加Zip方法(pr#222@Gitee) ### Bug修复 * 【cron 】 修复CronTimer可能死循环的问题(issue#1224@Github) 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 index 707983a14..148252b03 100644 --- a/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/compiler/CompilerUtil.java @@ -4,6 +4,7 @@ import javax.tools.DiagnosticListener; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** @@ -13,6 +14,7 @@ import javax.tools.ToolProvider; * @since 5.5.2 */ public class CompilerUtil { + /** * java 编译器 */ @@ -29,21 +31,21 @@ public class CompilerUtil { } /** - * 获取{@link JavaFileManager} + * 获取{@link StandardJavaFileManager} * - * @return {@link JavaFileManager} + * @return {@link StandardJavaFileManager} */ - public static JavaFileManager getFileManager() { + public static StandardJavaFileManager getFileManager() { return SYSTEM_COMPILER.getStandardFileManager(null, null, null); } /** * 新建编译任务 * - * @param fileManager {@link JavaFileManager},用于管理已经编译好的文件 + * @param fileManager {@link JavaFileManager},用于管理已经编译好的文件 * @param diagnosticListener 诊断监听 - * @param options 选项,例如 -cpXXX等 - * @param compilationUnits 编译单元,即需要编译的对象 + * @param options 选项,例如 -cpXXX等 + * @param compilationUnits 编译单元,即需要编译的对象 * @return {@link JavaCompiler.CompilationTask} */ public static JavaCompiler.CompilationTask getTask( @@ -53,4 +55,15 @@ public class CompilerUtil { Iterable 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/JavaClassFileManager.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaClassFileManager.java index 9960a7320..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 @@ -70,7 +70,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager { */ @Override public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) { - final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind); + 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/JavaSourceCompiler.java b/hutool-core/src/main/java/cn/hutool/core/compiler/JavaSourceCompiler.java index 2f9275cdc..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 @@ -2,6 +2,9 @@ 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; @@ -11,7 +14,6 @@ import cn.hutool.core.util.URLUtil; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler.CompilationTask; -import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import java.io.File; @@ -21,49 +23,55 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 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文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包 + * 待编译的资源,支持: + * + * + * 可以是 .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 父类加载器,null则使用默认类加载器 - */ - private JavaSourceCompiler(ClassLoader parent) { - this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader()); - } - - /** * 创建Java源码编译器 * @@ -74,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; } @@ -94,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; } @@ -138,15 +171,13 @@ public class JavaSourceCompiler { final List classPath = getClassPath(); final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0])); final URLClassLoader ucl = URLClassLoader.newInstance(urLs, this.parentClassLoader); - if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) { - // 没有需要编译的源码 + if (sourceList.isEmpty()) { + // 没有需要编译的源码文件返回加载zip或jar包的类加载器 return ucl; } - // 没有需要编译的源码文件返回加载zip或jar包的类加载器 - // 创建编译器 - final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager()); + final JavaClassFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager()); // classpath final List options = new ArrayList<>(); @@ -160,7 +191,7 @@ public class JavaSourceCompiler { final DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>(); final List javaFileObjectList = getJavaFileObject(); final CompilationTask task = CompilerUtil.getTask(javaFileManager, diagnosticCollector, options, javaFileObjectList); - try{ + try { if (task.call()) { // 加载编译后的类 return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT); @@ -180,7 +211,7 @@ public class JavaSourceCompiler { private List getClassPath() { List classPathFileList = new ArrayList<>(); for (File file : libraryFileList) { - List jarOrZipFile = FileUtil.loopFiles(file, (subFile)-> JavaFileObjectUtil.isJarOrZipFile(subFile.getName())); + List jarOrZipFile = FileUtil.loopFiles(file, (subFile) -> JavaFileObjectUtil.isJarOrZipFile(subFile.getName())); classPathFileList.addAll(jarOrZipFile); if (file.isDirectory()) { classPathFileList.add(file); @@ -195,16 +226,18 @@ public class JavaSourceCompiler { * @return 待编译的Java文件对象 */ private List getJavaFileObject() { - final List collection = new ArrayList<>(); + final List list = new ArrayList<>(); - // 源码文件 - for (File file : sourceFileList) { - FileUtil.walkFiles(file, (subFile)-> collection.addAll(JavaFileObjectUtil.getJavaFileObjects(file))); + 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; } /** 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 cf8a4e707..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; @@ -12,8 +14,8 @@ import java.nio.charset.Charset; /** * Java 源码文件对象,支持:
*
    - *
  1. 源文件
  2. - *
  3. 代码内容
  4. + *
  5. 源文件,通过文件的uri传入
  6. + *
  7. 代码内容,通过流传入
  8. *
* * @author lzpeng @@ -27,7 +29,7 @@ class JavaSourceFileObject extends SimpleJavaFileObject { private InputStream inputStream; /** - * 构造 + * 构造,支持File等路径类型的源码 * * @param uri 需要编译的文件uri */ @@ -36,7 +38,7 @@ class JavaSourceFileObject extends SimpleJavaFileObject { } /** - * 构造 + * 构造,支持String类型的源码 * * @param className 需要编译的类名 * @param code 需要编译的类源码 @@ -46,13 +48,13 @@ class JavaSourceFileObject extends SimpleJavaFileObject { } /** - * 构造 + * 构造,支持流中读取源码(例如zip或网络等) * * @param name 需要编译的文件名 * @param inputStream 输入流 */ protected JavaSourceFileObject(String name, InputStream inputStream) { - this(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension)); + 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 6780b93d4..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 @@ -183,7 +183,11 @@ public class FileUtil extends PathUtil { } /** - * 递归遍历目录并处理目录下的文件 + * 递归遍历目录并处理目录下的文件,可以处理目录或文件: + *
    + *
  • 非目录则直接调用{@link Consumer}处理
  • + *
  • 目录则递归调用此方法处理
  • + *
* * @param file 文件或目录,文件直接处理 * @param consumer 文件处理器,只会处理文件 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/Resource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/Resource.java index a174c9dce..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,86 +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} */ - default BufferedReader getReader(Charset charset){ + default BufferedReader getReader(Charset charset) { return IoUtil.getReader(getStream(), charset); } - + /** * 读取资源内容,读取完毕后会关闭流
* 关闭流并不影响下一次读取 - * + * * @param charset 编码 * @return 读取资源内容 * @throws IORuntimeException 包装{@link IOException} */ - default 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 */ - default byte[] readBytes() throws IORuntimeException{ + default byte[] readBytes() throws IORuntimeException { return IoUtil.readBytes(getStream()); } } 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 0e63d35e1..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 @@ -355,23 +355,53 @@ 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 !"); } - - ZipOutputStream out = null; - try { - out = getZipOutputStream(zipFile, charset); - for (int i = 0; i < paths.length; i++) { - add(ins[i], paths[i], out); - } - } finally { - IoUtil.close(out); + for (int i = 0; i < paths.length; i++) { + add(ins[i], paths[i], zipOutputStream); } - return zipFile; } /** @@ -1056,10 +1086,10 @@ public class ZipUtil { * @param file 需要压缩的文件 * @param path 在压缩文件中的路径 * @param out 压缩文件存储对象 - * @throws UtilException IO异常 + * @throws IORuntimeException IO异常 * @since 4.0.5 */ - private static void add(File file, String path, ZipOutputStream out) throws UtilException { + private static void add(File file, String path, ZipOutputStream out) throws IORuntimeException { add(FileUtil.getInputStream(file), path, out); } @@ -1069,9 +1099,9 @@ public class ZipUtil { * @param in 需要压缩的输入流,使用完后自动关闭 * @param path 压缩的路径 * @param out 压缩文件存储对象 - * @throws UtilException IO异常 + * @throws IORuntimeException IO异常 */ - private static void add(InputStream in, String path, ZipOutputStream out) throws UtilException { + private static void add(InputStream in, String path, ZipOutputStream out) throws IORuntimeException { if (null == in) { return; } @@ -1079,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); 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 eab36a616..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,6 +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; @@ -10,6 +12,11 @@ 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 @@ -19,4 +26,11 @@ public class ResourceUtilTest { 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()); + } }