diff --git a/CHANGELOG.md b/CHANGELOG.md index 663c45c01..0b36313c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * 【core 】 NumberUtil的decimalFormat增加数字检查 * 【http 】 HttpBase的httpVersion方法设置为无效(issue#1644@Github) * 【extra 】 Sftp增加download重载(issue#I3VBSL@Gitee) +* 【cache 】 修改FIFOCache初始大小(issue#1647@Github) ### 🐞Bug修复 * 【db 】 修复count方法丢失参数问题(issue#I3VBSL@Gitee) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java index 40e7ce8ef..64aa76e94 100644 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java @@ -37,7 +37,7 @@ public class FIFOCache extends AbstractCache { public FIFOCache(int capacity, long timeout) { this.capacity = capacity; this.timeout = timeout; - cacheMap = new LinkedHashMap<>(Math.max(1 << 4, capacity >>> 7), 1.0f, false); + cacheMap = new LinkedHashMap<>(capacity + 1, 1.0f, false); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java b/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java new file mode 100644 index 000000000..25b966d54 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/AppendableWriter.java @@ -0,0 +1,106 @@ +package cn.hutool.core.io; + +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.Writer; +import java.nio.CharBuffer; + +/** + * 同时继承{@link Writer}和实现{@link Appendable}的聚合类,用于适配两种接口操作 + * 实现来自:jodd + * + * @author looly,jodd + * @since 5.7.0 + */ +public class AppendableWriter extends Writer implements Appendable { + + private final Appendable appendable; + private final boolean flushable; + private boolean closed; + + public AppendableWriter(final Appendable appendable) { + this.appendable = appendable; + this.flushable = appendable instanceof Flushable; + this.closed = false; + } + + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + checkNotClosed(); + appendable.append(CharBuffer.wrap(cbuf), off, off + len); + } + + @Override + public void write(final int c) throws IOException { + checkNotClosed(); + appendable.append((char) c); + } + + @Override + public Writer append(final char c) throws IOException { + checkNotClosed(); + appendable.append(c); + return this; + } + + @Override + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { + checkNotClosed(); + appendable.append(csq, start, end); + return this; + } + + @Override + public Writer append(final CharSequence csq) throws IOException { + checkNotClosed(); + appendable.append(csq); + return this; + } + + @Override + public void write(final String str, final int off, final int len) throws IOException { + checkNotClosed(); + appendable.append(str, off, off + len); + } + + @Override + public void write(final String str) throws IOException { + appendable.append(str); + } + + @Override + public void write(final char[] cbuf) throws IOException { + appendable.append(CharBuffer.wrap(cbuf)); + } + + @Override + public void flush() throws IOException { + checkNotClosed(); + if (flushable) { + ((Flushable) appendable).flush(); + } + } + + /** + * 检查Writer是否已经被关闭 + * + * @throws IOException IO异常 + */ + private void checkNotClosed() throws IOException { + if (closed) { + throw new IOException("Writer is closed!" + this); + } + } + + @Override + public void close() throws IOException { + if (false == closed) { + flush(); + if (appendable instanceof Closeable) { + ((Closeable) appendable).close(); + } + closed = true; + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/io/ManifestUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/ManifestUtil.java new file mode 100644 index 000000000..818d85d9e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/io/ManifestUtil.java @@ -0,0 +1,58 @@ +package cn.hutool.core.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * Jar包中manifest.mf文件获取和解析工具类 + * 来自Jodd + * + * @author looly, jodd + * @since 5.7.0 + */ +public class ManifestUtil { + private static final String[] MANIFEST_NAMES = {"Manifest.mf", "manifest.mf", "MANIFEST.MF"}; + + public static Manifest getManifest(File classpathItem) { + Manifest manifest = null; + + if (classpathItem.isFile()) { + JarFile jar = null; + try { + jar = new JarFile(classpathItem); + manifest = jar.getManifest(); + } catch (final IOException ignore) { + } finally { + IoUtil.close(jar); + } + } else { + final File metaDir = new File(classpathItem, "META-INF"); + File manifestFile = null; + if (metaDir.isDirectory()) { + for (final String name : MANIFEST_NAMES) { + final File mFile = new File(metaDir, name); + if (mFile.isFile()) { + manifestFile = mFile; + break; + } + } + } + if (manifestFile != null) { + FileInputStream fis = null; + try { + fis = new FileInputStream(manifestFile); + manifest = new Manifest(fis); + } catch (final IOException ignore) { + } + finally { + IoUtil.close(fis); + } + } + } + + return manifest; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java index 7157980c8..e2a901b8b 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ClassLoaderUtil.java @@ -8,6 +8,8 @@ import cn.hutool.core.lang.SimpleCache; import java.io.File; import java.lang.reflect.Array; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -21,18 +23,30 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ClassLoaderUtil { - /** 数组类的结尾符: "[]" */ + /** + * 数组类的结尾符: "[]" + */ private static final String ARRAY_SUFFIX = "[]"; - /** 内部数组类名前缀: "[" */ + /** + * 内部数组类名前缀: "[" + */ private static final String INTERNAL_ARRAY_PREFIX = "["; - /** 内部非原始类型类名前缀: "[L" */ + /** + * 内部非原始类型类名前缀: "[L" + */ private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; - /** 包名分界符: '.' */ + /** + * 包名分界符: '.' + */ private static final char PACKAGE_SEPARATOR = StrUtil.C_DOT; - /** 内部类分界符: '$' */ + /** + * 内部类分界符: '$' + */ private static final char INNER_CLASS_SEPARATOR = '$'; - /** 原始类型名和其class对应表,例如:int =》 int.class */ + /** + * 原始类型名和其class对应表,例如:int =》 int.class + */ private static final Map> PRIMITIVE_TYPE_NAME_MAP = new ConcurrentHashMap<>(32); private static final SimpleCache> CLASS_CACHE = new SimpleCache<>(); @@ -62,9 +76,33 @@ public class ClassLoaderUtil { * @see Thread#getContextClassLoader() */ public static ClassLoader getContextClassLoader() { - return Thread.currentThread().getContextClassLoader(); + if (System.getSecurityManager() == null) { + return Thread.currentThread().getContextClassLoader(); + } else { + // 绕开权限检查 + return AccessController.doPrivileged( + (PrivilegedAction) () -> Thread.currentThread().getContextClassLoader()); + } } + /** + * 获取系统{@link ClassLoader} + * + * @return 系统{@link ClassLoader} + * @see ClassLoader#getSystemClassLoader() + * @since 5.7.0 + */ + public static ClassLoader getSystemClassLoader() { + if (System.getSecurityManager() == null) { + return ClassLoader.getSystemClassLoader(); + } else { + // 绕开权限检查 + return AccessController.doPrivileged( + (PrivilegedAction) ClassLoader::getSystemClassLoader); + } + } + + /** * 获取{@link ClassLoader}
* 获取顺序如下:
@@ -82,13 +120,14 @@ public class ClassLoaderUtil { if (classLoader == null) { classLoader = ClassLoaderUtil.class.getClassLoader(); if (null == classLoader) { - classLoader = ClassLoader.getSystemClassLoader(); + classLoader = getSystemClassLoader(); } } return classLoader; } // ----------------------------------------------------------------------------------- loadClass + /** * 加载类,通过传入类的字符串,返回其对应的类名,使用默认ClassLoader并初始化类(调用static模块内容和初始化static属性)
* 扩展{@link Class#forName(String, boolean, ClassLoader)}方法,支持以下几类类名的加载: @@ -117,7 +156,7 @@ public class ClassLoaderUtil { * 3、内部类,例如:java.lang.Thread.State会被转为java.lang.Thread$State加载 * * - * @param name 类名 + * @param name 类名 * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性) * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 @@ -138,8 +177,8 @@ public class ClassLoaderUtil { * 3、内部类,例如:java.lang.Thread.State会被转为java.lang.Thread$State加载 * * - * @param name 类名 - * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader + * @param name 类名 + * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性) * @return 类名对应的类 * @throws UtilException 包装{@link ClassNotFoundException},没有类名对应的类时抛出此异常 @@ -223,7 +262,7 @@ public class ClassLoaderUtil { * 加载外部类 * * @param jarOrDir jar文件或者包含jar和class文件的目录 - * @param name 类名 + * @param name 类名 * @return 类 * @since 4.4.2 */ @@ -236,6 +275,7 @@ public class ClassLoaderUtil { } // ----------------------------------------------------------------------------------- isPresent + /** * 指定类是否被提供,使用默认ClassLoader
* 通过调用{@link #loadClass(String, ClassLoader, boolean)}方法尝试加载指定类名的类,如果加载失败返回false
@@ -253,7 +293,7 @@ public class ClassLoaderUtil { * 通过调用{@link #loadClass(String, ClassLoader, boolean)}方法尝试加载指定类名的类,如果加载失败返回false
* 加载失败的原因可能是此类不存在或其关联引用类不存在 * - * @param className 类名 + * @param className 类名 * @param classLoader {@link ClassLoader} * @return 是否被提供 */ @@ -267,11 +307,12 @@ public class ClassLoaderUtil { } // ----------------------------------------------------------------------------------- Private method start + /** * 尝试转换并加载内部类,例如java.lang.Thread.State =》java.lang.Thread$State * - * @param name 类名 - * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader + * @param name 类名 + * @param classLoader {@link ClassLoader},{@code null} 则使用系统默认ClassLoader * @param isInitialized 是否初始化类(调用static模块内容和初始化static属性) * @return 类名对应的类 * @since 4.1.20