From bd123386cef924b8ff9a797c95324965f9349c5c Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 29 Aug 2025 11:52:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D`ReflectUtil`=E4=B8=AD?= =?UTF-8?q?=E5=9B=A0class=E5=92=8CMethod=E5=85=B3=E8=81=94=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E7=BC=93=E5=AD=98=E6=97=A0=E6=B3=95=E5=9B=9E?= =?UTF-8?q?=E6=94=B6=E9=97=AE=E9=A2=98=EF=BC=88issue#4039@Github=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 +- .../java/cn/hutool/cache/impl/WeakCache.java | 13 +- .../cn/hutool/core/bean/BeanDescCache.java | 4 +- .../cn/hutool/core/bean/BeanInfoCache.java | 8 +- .../core/convert/impl/EnumConverter.java | 4 +- .../java/cn/hutool/core/img/ColorUtil.java | 1 - .../java/cn/hutool/core/io/file/PathUtil.java | 1 - .../java/cn/hutool/core/lang/PatternPool.java | 4 +- .../java/cn/hutool/core/lang/SimpleCache.java | 6 +- .../cn/hutool/core/lang/func/LambdaUtil.java | 4 +- .../hutool/core/lang/intern/WeakInterner.java | 4 +- .../cn/hutool/core/lang/ref/PhantomObj.java | 43 ++ .../java/cn/hutool/core/lang/ref/Ref.java | 18 + .../hutool/core/lang/ref/ReferenceType.java | 29 ++ .../java/cn/hutool/core/lang/ref/SoftObj.java | 40 ++ .../cn/hutool/core/lang/ref/StrongObj.java | 42 ++ .../java/cn/hutool/core/lang/ref/WeakObj.java | 42 ++ .../cn/hutool/core/lang/ref/package-info.java | 10 + .../lang/reflect/ActualTypeMapperPool.java | 4 +- .../main/java/cn/hutool/core/map/MapUtil.java | 40 +- .../core/map/ReferenceConcurrentMap.java | 1 + .../cn/hutool/core/map/WeakConcurrentMap.java | 1 + .../map/reference/ReferenceConcurrentMap.java | 423 ++++++++++++++++++ .../core/map/reference/SoftConcurrentMap.java | 63 +++ .../map/reference/WeakKeyConcurrentMap.java | 64 +++ .../reference/WeakKeyValueConcurrentMap.java | 63 +++ .../core/map/reference/package-info.java | 23 + .../main/java/cn/hutool/core/net/RFC3986.java | 4 - .../java/cn/hutool/core/util/ObjectUtil.java | 28 ++ .../cn/hutool/core/util/ReferenceUtil.java | 32 +- .../java/cn/hutool/core/util/ReflectUtil.java | 30 +- .../java/cn/hutool/core/util/URLUtil.java | 2 - .../core/map/WeakConcurrentMapTest.java | 9 +- .../cn/hutool/core/util/ReflectUtilTest.java | 17 +- .../hutool/extra/cglib/BeanCopierCache.java | 4 +- .../extra/servlet/JakartaServletUtil.java | 1 - .../cn/hutool/extra/servlet/ServletUtil.java | 1 - .../java/cn/hutool/script/ScriptUtil.java | 15 +- 38 files changed, 1015 insertions(+), 91 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/PhantomObj.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/ReferenceType.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/SoftObj.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/StrongObj.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/WeakObj.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/lang/ref/package-info.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/reference/ReferenceConcurrentMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/reference/SoftConcurrentMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyConcurrentMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyValueConcurrentMap.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/map/reference/package-info.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b35836b2..b16c1005e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,16 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.8.41(2025-08-26) +# 5.8.41(2025-08-29) ### 🐣新特性 +* 【core 】 增加`WeakKeyValueConcurrentMap`及其关联类,同时废弃`WeakConcurrentMap`并替换(issue#4039@Github) +* 【core 】 `MapUtil`增加`removeByValue`和`removeIf`方法 +* 【core 】 `ObjectUtil`增加`apply`方法 +* 【core 】 `ReferenceUtil`增加`get`方法 + ### 🐞Bug修复 +* 【core 】 修复`ReflectUtil`中因class和Method关联导致的缓存无法回收问题(issue#4039@Github) ------------------------------------------------------------------------------------------------------------- # 5.8.40(2025-08-26) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java index a6fb23690..37150588e 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java @@ -3,9 +3,8 @@ package cn.hutool.cache.impl; import cn.hutool.cache.CacheListener; import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.mutable.Mutable; -import cn.hutool.core.map.WeakConcurrentMap; - -import java.lang.ref.Reference; +import cn.hutool.core.lang.ref.Ref; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; /** * 弱引用缓存
@@ -27,16 +26,18 @@ public class WeakCache extends TimedCache{ * @param timeout 超时时常,单位毫秒,-1或0表示无限制 */ public WeakCache(long timeout) { - super(timeout, new WeakConcurrentMap<>()); + super(timeout, new WeakKeyValueConcurrentMap<>()); } @Override public WeakCache setListener(CacheListener listener) { super.setListener(listener); - final WeakConcurrentMap, CacheObj> map = (WeakConcurrentMap, CacheObj>) this.cacheMap; + final WeakKeyValueConcurrentMap, CacheObj> map = (WeakKeyValueConcurrentMap, CacheObj>) this.cacheMap; // WeakKey回收之后,key对应的值已经是null了,因此此处的key也为null - map.setPurgeListener((key, value)-> listener.onRemove(Opt.ofNullable(key).map(Reference::get).map(Mutable::get).get(), value.getValue())); + map.setPurgeListener((key, value)-> listener.onRemove( + Opt.ofNullable(key).map(Ref::get).map(Mutable::get).get(), + Opt.ofNullable(value).map(Ref::get).map(CacheObj::getValue).get())); return this; } diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java index fa29e1d94..3e38e776c 100755 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanDescCache.java @@ -1,7 +1,7 @@ package cn.hutool.core.bean; import cn.hutool.core.lang.func.Func0; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; /** * Bean属性缓存
@@ -12,7 +12,7 @@ import cn.hutool.core.map.WeakConcurrentMap; public enum BeanDescCache { INSTANCE; - private final WeakConcurrentMap, BeanDesc> bdCache = new WeakConcurrentMap<>(); + private final WeakKeyValueConcurrentMap, BeanDesc> bdCache = new WeakKeyValueConcurrentMap<>(); /** * 获得属性名和{@link BeanDesc}Map映射 diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java index 63fc224d5..2cd29c3e6 100755 --- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanInfoCache.java @@ -1,8 +1,8 @@ package cn.hutool.core.bean; import cn.hutool.core.lang.func.Func0; -import cn.hutool.core.map.ReferenceConcurrentMap; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.ReferenceConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import java.beans.PropertyDescriptor; import java.util.Map; @@ -16,8 +16,8 @@ import java.util.Map; public enum BeanInfoCache { INSTANCE; - private final WeakConcurrentMap, Map> pdCache = new WeakConcurrentMap<>(); - private final WeakConcurrentMap, Map> ignoreCasePdCache = new WeakConcurrentMap<>(); + private final WeakKeyValueConcurrentMap, Map> pdCache = new WeakKeyValueConcurrentMap<>(); + private final WeakKeyValueConcurrentMap, Map> ignoreCasePdCache = new WeakKeyValueConcurrentMap<>(); /** * 获得属性名和{@link PropertyDescriptor}Map映射 diff --git a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java index d73a458ae..c360cfeab 100755 --- a/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java +++ b/hutool-core/src/main/java/cn/hutool/core/convert/impl/EnumConverter.java @@ -4,7 +4,7 @@ import cn.hutool.core.convert.AbstractConverter; import cn.hutool.core.convert.ConvertException; import cn.hutool.core.lang.EnumItem; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.ModifierUtil; @@ -25,7 +25,7 @@ import java.util.stream.Collectors; public class EnumConverter extends AbstractConverter { private static final long serialVersionUID = 1L; - private static final WeakConcurrentMap, Map, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap, Map, Method>> VALUE_OF_METHOD_CACHE = new WeakKeyValueConcurrentMap<>(); private final Class enumClass; diff --git a/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java b/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java index d78e5ea95..ff8306320 100644 --- a/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/img/ColorUtil.java @@ -204,7 +204,6 @@ public class ColorUtil { * * @param color 指定颜色 * @return 其余颜色与color的最大距离 - * @since 6.0.0-M16 */ public static int maxDistance(final Color color) { if (null == color) { 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 ff270d304..60dfd6c28 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 @@ -735,7 +735,6 @@ public class PathUtil { * @param dir 临时文件创建的所在目录 * @return 临时文件 * @throws IORuntimeException IO异常 - * @since 6.0.0 */ public static Path createTempFile(final String prefix, final String suffix, final Path dir) throws IORuntimeException { int exceptionsCount = 0; diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java index a4c6cbda3..8c5ef4e2f 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/PatternPool.java @@ -1,6 +1,6 @@ package cn.hutool.core.lang; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import java.util.regex.Pattern; @@ -196,7 +196,7 @@ public class PatternPool { /** * Pattern池 */ - private static final WeakConcurrentMap POOL = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap POOL = new WeakKeyValueConcurrentMap<>(); /** * 先从Pattern池中查找正则对应的{@link Pattern},找不到则编译正则表达式并入池。 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java b/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java index 67e9781b9..712006a6a 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/SimpleCache.java @@ -6,7 +6,7 @@ import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.map.SafeConcurrentHashMap; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import java.io.Serializable; import java.util.Iterator; @@ -19,7 +19,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; /** - * 简单缓存,无超时实现,默认使用{@link WeakConcurrentMap}实现缓存自动清理 + * 简单缓存,无超时实现,默认使用{@link WeakKeyValueConcurrentMap}实现缓存自动清理 * * @param 键类型 * @param 值类型 @@ -43,7 +43,7 @@ public class SimpleCache implements Iterable>, Serializabl * 构造,默认使用{@link WeakHashMap}实现缓存自动清理 */ public SimpleCache() { - this(new WeakConcurrentMap<>()); + this(new WeakKeyValueConcurrentMap<>()); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java index 701737367..daafc07ac 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/func/LambdaUtil.java @@ -1,7 +1,7 @@ package cn.hutool.core.lang.func; import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.util.ClassUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; @@ -18,7 +18,7 @@ import java.lang.invoke.SerializedLambda; */ public class LambdaUtil { - private static final WeakConcurrentMap cache = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap cache = new WeakKeyValueConcurrentMap<>(); /** * 通过对象的方法或类的静态方法引用,获取lambda实现类 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java b/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java index 335828950..ce9f1c6fc 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/intern/WeakInterner.java @@ -1,6 +1,6 @@ package cn.hutool.core.lang.intern; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import java.lang.ref.WeakReference; @@ -12,7 +12,7 @@ import java.lang.ref.WeakReference; */ public class WeakInterner implements Interner{ - private final WeakConcurrentMap> cache = new WeakConcurrentMap<>(); + private final WeakKeyValueConcurrentMap> cache = new WeakKeyValueConcurrentMap<>(); public T intern(T sample) { if (sample == null) { diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/PhantomObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/PhantomObj.java new file mode 100644 index 000000000..72b6c3f1b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/PhantomObj.java @@ -0,0 +1,43 @@ +package cn.hutool.core.lang.ref; + +import cn.hutool.core.util.ObjUtil; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.util.Objects; + +/** + * 虚引用对象,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。
+ * 此时对象未被真正回收,要等到{@link ReferenceQueue}被真正处理后才会被回收。 + * + * @param 键类型 + */ +public class PhantomObj extends PhantomReference implements Ref{ + private final int hashCode; + + /** + * 构造 + * + * @param obj 原始对象 + * @param queue {@link ReferenceQueue} + */ + public PhantomObj(final T obj, final ReferenceQueue queue) { + super(obj, queue); + hashCode = Objects.hashCode(obj); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (other instanceof PhantomObj) { + return ObjUtil.equals(((PhantomObj) other).get(), get()); + } + return false; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java new file mode 100644 index 000000000..38256c596 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java @@ -0,0 +1,18 @@ +package cn.hutool.core.lang.ref; + +/** + * 针对{@link java.lang.ref.Reference}的接口定义,用于扩展功能
+ * 例如提供自定义的无需回收对象 + * + * @param 对象类型 + */ +@FunctionalInterface +public interface Ref { + + /** + * 获取引用的原始对象 + * + * @return 原始对象 + */ + T get(); +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/ReferenceType.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/ReferenceType.java new file mode 100644 index 000000000..276d2c873 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/ReferenceType.java @@ -0,0 +1,29 @@ +package cn.hutool.core.lang.ref; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; + +/** + * 引用类型 + * + * @author Looly + */ +public enum ReferenceType { + /** + * 强引用,不回收 + */ + STRONG, + /** + * 软引用,在GC报告内存不足时会被GC回收 + */ + SOFT, + /** + * 弱引用,在GC时发现弱引用会回收其对象 + */ + WEAK, + /** + * 虚引用,在GC时发现虚引用对象,会将{@link PhantomReference}插入{@link ReferenceQueue}。
+ * 此时对象未被真正回收,要等到{@link ReferenceQueue}被真正处理后才会被回收。 + */ + PHANTOM +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/SoftObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/SoftObj.java new file mode 100644 index 000000000..e80b33104 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/SoftObj.java @@ -0,0 +1,40 @@ +package cn.hutool.core.lang.ref; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.Objects; + +/** + * 软引用对象,在GC报告内存不足时会被GC回收 + * + * @param 键类型 + */ +public class SoftObj extends SoftReference implements Ref{ + private final int hashCode; + + /** + * 构造 + * + * @param obj 原始对象 + * @param queue {@link ReferenceQueue} + */ + public SoftObj(final T obj, final ReferenceQueue queue) { + super(obj, queue); + hashCode = Objects.hashCode(obj); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (other instanceof SoftObj) { + return Objects.equals(((SoftObj) other).get(), get()); + } + return false; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/StrongObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/StrongObj.java new file mode 100644 index 000000000..9f43d20ad --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/StrongObj.java @@ -0,0 +1,42 @@ +package cn.hutool.core.lang.ref; + +import java.util.Objects; + +/** + * 弱引用对象,在GC时发现弱引用会回收其对象 + * + * @param 键类型 + */ +public class StrongObj implements Ref { + + private final T obj; + + /** + * 构造 + * + * @param obj 原始对象 + */ + public StrongObj(final T obj) { + this.obj = obj; + } + + @Override + public T get() { + return this.obj; + } + + @Override + public int hashCode() { + return Objects.hashCode(obj); + } + + @Override + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (other instanceof StrongObj) { + return Objects.equals(((StrongObj) other).get(), get()); + } + return false; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/WeakObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/WeakObj.java new file mode 100644 index 000000000..dd5bd9ad6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/WeakObj.java @@ -0,0 +1,42 @@ +package cn.hutool.core.lang.ref; + +import cn.hutool.core.util.ObjUtil; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Objects; + +/** + * 弱引用对象,在GC时发现弱引用会回收其对象 + * + * @param 键类型 + */ +public class WeakObj extends WeakReference implements Ref{ + private final int hashCode; + + /** + * 构造 + * + * @param obj 原始对象 + * @param queue {@link ReferenceQueue} + */ + public WeakObj(final T obj, final ReferenceQueue queue) { + super(obj, queue); + hashCode = Objects.hashCode(obj); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (other instanceof WeakObj) { + return ObjUtil.equals(((WeakObj) other).get(), get()); + } + return false; + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/ref/package-info.java b/hutool-core/src/main/java/cn/hutool/core/lang/ref/package-info.java new file mode 100644 index 000000000..4534bdabf --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/lang/ref/package-info.java @@ -0,0 +1,10 @@ +/** + * 引用工具封装,主要针对{@link java.lang.ref.Reference} 工具化封装
+ * 主要封装包括: + *
+ * 1. {@link java.lang.ref.SoftReference} 软引用,在GC报告内存不足时会被GC回收
+ * 2. {@link java.lang.ref.WeakReference} 弱引用,在GC时发现弱引用会回收其对象
+ * 3. {@link java.lang.ref.PhantomReference} 虚引用,在GC时发现虚引用对象,会将{@link java.lang.ref.PhantomReference}插入{@link java.lang.ref.ReferenceQueue}。 此时对象未被真正回收,要等到{@link java.lang.ref.ReferenceQueue}被真正处理后才会被回收。
+ * 
+ */ +package cn.hutool.core.lang.ref; diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java index 136577c30..8081e721c 100755 --- a/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/reflect/ActualTypeMapperPool.java @@ -1,7 +1,7 @@ package cn.hutool.core.lang.reflect; import cn.hutool.core.convert.Convert; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.TypeUtil; @@ -20,7 +20,7 @@ import java.util.Map; */ public class ActualTypeMapperPool { - private static final WeakConcurrentMap> CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap> CACHE = new WeakKeyValueConcurrentMap<>(); /** * 获取泛型变量和泛型实际类型的对应关系Map diff --git a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java index 1128b510d..28ca714bc 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java @@ -5,16 +5,14 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.*; import cn.hutool.core.stream.CollectorUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.JdkUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.*; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Predicate; /** * Map相关工具类 @@ -1347,6 +1345,40 @@ public class MapUtil { return map; } + /** + * 去除Map中值为指定值的键值对
+ * 注意:此方法在传入的Map上直接修改。 + * + * @param key的类型 + * @param value的类型 + * @param map Map + * @param value 给定值 + * @return map + * @since 5.8.41 + */ + public static Map removeByValue(final Map map, final V value) { + return removeIf(map, entry -> ObjUtil.equals(value, entry.getValue())); + } + + /** + * 去除Map中值为{@code null}的键值对
+ * 注意:此方法在传入的Map上直接修改。 + * + * @param key的类型 + * @param value的类型 + * @param map Map + * @param predicate 移除条件,当{@link Predicate#test(Object)}为{@code true}时移除 + * @return map + * @since 5.8.41 + */ + public static Map removeIf(final Map map, final Predicate> predicate) { + if (isEmpty(map)) { + return map; + } + map.entrySet().removeIf(predicate); + return map; + } + /** * 返回一个空Map * diff --git a/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java index cc5ba04f8..b0cd9e78b 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/ReferenceConcurrentMap.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; * @param 值类型 * @author looly * @since 5.8.0 + * @deprecated 请使用{@link cn.hutool.core.map.reference.ReferenceConcurrentMap} */ public class ReferenceConcurrentMap implements ConcurrentMap, Iterable>, Serializable { diff --git a/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java index 89369609e..fba82f6f1 100755 --- a/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java +++ b/hutool-core/src/main/java/cn/hutool/core/map/WeakConcurrentMap.java @@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentMap; * @param 值类型 * @author looly * @since 5.8.0 + * @deprecated 请使用{@link cn.hutool.core.map.reference.WeakKeyConcurrentMap} */ public class WeakConcurrentMap extends ReferenceConcurrentMap { diff --git a/hutool-core/src/main/java/cn/hutool/core/map/reference/ReferenceConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/reference/ReferenceConcurrentMap.java new file mode 100644 index 000000000..3ec2ee921 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/reference/ReferenceConcurrentMap.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2013-2025 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hutool.core.map.reference; + +import cn.hutool.core.lang.ref.Ref; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReferenceUtil; + +import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.*; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * 线程安全的ReferenceMap实现 + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.41 + */ +public abstract class ReferenceConcurrentMap implements ConcurrentMap, Iterable>, Serializable { + private static final long serialVersionUID = 1L; + + /** + * 键值对引用 + */ + final ConcurrentMap, Ref> raw; + /** + * 因无强链接而清除的键队列 + */ + private final ReferenceQueue lastKeyQueue; + /** + * 因无强链接而清除的值队列 + */ + private final ReferenceQueue lastValueQueue; + /** + * 回收监听 + */ + private BiConsumer, Ref> purgeListener; + + // region 构造 + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + */ + public ReferenceConcurrentMap(final ConcurrentMap, Ref> raw) { + this.raw = raw; + lastKeyQueue = new ReferenceQueue<>(); + lastValueQueue = new ReferenceQueue<>(); + } + // endregion + + /** + * 设置对象回收清除监听 + * + * @param purgeListener 监听函数 + */ + public void setPurgeListener(final BiConsumer, Ref> purgeListener) { + this.purgeListener = purgeListener; + } + + @Override + public int size() { + this.purgeStale(); + return this.raw.size(); + } + + @Override + public boolean isEmpty() { + this.purgeStale(); + return this.raw.isEmpty(); + } + + @Override + public V get(final Object key) { + this.purgeStale(); + return unwrap(this.raw.get(wrapKey(key))); + } + + @Override + public boolean containsKey(final Object key) { + this.purgeStale(); + return this.raw.containsKey(wrapKey(key)); + } + + @Override + public boolean containsValue(final Object value) { + this.purgeStale(); + return this.raw.containsValue(wrapValue(value)); + } + + @Override + public V put(final K key, final V value) { + this.purgeStale(); + final Ref vReference = this.raw.put(wrapKey(key), wrapValue(value)); + return unwrap(vReference); + } + + @Override + public V putIfAbsent(final K key, final V value) { + this.purgeStale(); + final Ref vReference = this.raw.putIfAbsent(wrapKey(key), wrapValue(value)); + return unwrap(vReference); + } + + @Override + public void putAll(final Map m) { + m.forEach(this::put); + } + + @Override + public V replace(final K key, final V value) { + this.purgeStale(); + final Ref vReference = this.raw.replace(wrapKey(key), wrapValue(value)); + return unwrap(vReference); + } + + @Override + public boolean replace(final K key, final V oldValue, final V newValue) { + this.purgeStale(); + return this.raw.replace(wrapKey(key), wrapValue(oldValue), wrapValue(newValue)); + } + + @Override + public void replaceAll(final BiFunction function) { + this.purgeStale(); + this.raw.replaceAll((rKey, rValue) -> wrapValue(function.apply(unwrap(rKey), unwrap(rValue)))); + } + + @Override + public V computeIfAbsent(final K key, final Function mappingFunction) { + V result = null; + while(null == result){ + this.purgeStale(); + final Ref vReference = this.raw.computeIfAbsent(wrapKey(key), + kReference -> wrapValue(mappingFunction.apply(unwrap(kReference)))); + + // issue#IA5GMH 如果vReference在此时被GC回收,则unwrap后为null,需要循环计算 + // 但是当用户提供的值本身为null,则直接返回之 + if(NullRef.NULL == vReference){ + // 用户提供的值本身为null + return null; + } + result = unwrap(vReference); + } + return result; + } + + @Override + public V computeIfPresent(final K key, final BiFunction remappingFunction) { + V result = null; + while(null == result){ + this.purgeStale(); + final Ref vReference = this.raw.computeIfPresent(wrapKey(key), + (kReference, vReference1) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference1)))); + + // issue#IA5GMH 如果vReference在此时被GC回收,则unwrap后为null,需要循环计算 + // 但是当用户提供的值本身为null,则直接返回之 + if(NullRef.NULL == vReference){ + // 用户提供的值本身为null + return null; + } + result = unwrap(vReference); + } + return result; + } + + @Override + public V remove(final Object key) { + this.purgeStale(); + return unwrap(this.raw.remove(wrapKey(key))); + } + + @SuppressWarnings("unchecked") + @Override + public boolean remove(final Object key, final Object value) { + this.purgeStale(); + return this.raw.remove(wrapKey((K) key, null), value); + } + + @SuppressWarnings("StatementWithEmptyBody") + @Override + public void clear() { + this.raw.clear(); + while (lastKeyQueue.poll() != null) ; + while (lastValueQueue.poll() != null) ; + } + + @Override + public Set keySet() { + this.purgeStale(); + final Set> referenceSet = this.raw.keySet(); + return new AbstractSet() { + @Override + public Iterator iterator() { + final Iterator> referenceIter = referenceSet.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return referenceIter.hasNext(); + } + + @Override + public K next() { + return unwrap(referenceIter.next()); + } + }; + } + + @Override + public int size() { + return referenceSet.size(); + } + }; + } + + @Override + public Collection values() { + this.purgeStale(); + final Collection> referenceValues = this.raw.values(); + return new AbstractCollection() { + @Override + public Iterator iterator() { + final Iterator> referenceIter = referenceValues.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return referenceIter.hasNext(); + } + + @Override + public V next() { + return unwrap(referenceIter.next()); + } + }; + } + + @Override + public int size() { + return referenceValues.size(); + } + }; + } + + @Override + public Set> entrySet() { + this.purgeStale(); + final Set, Ref>> referenceEntrySet = this.raw.entrySet(); + return new AbstractSet>() { + @Override + public Iterator> iterator() { + final Iterator, Ref>> referenceIter = referenceEntrySet.iterator(); + return new Iterator>() { + @Override + public boolean hasNext() { + return referenceIter.hasNext(); + } + + @Override + public Entry next() { + final Entry, Ref> next = referenceIter.next(); + return new Entry() { + @Override + public K getKey() { + return unwrap(next.getKey()); + } + + @Override + public V getValue() { + return unwrap(next.getValue()); + } + + @Override + public V setValue(final V value) { + return unwrap(next.setValue(wrapValue(value))); + } + }; + } + }; + } + + @Override + public int size() { + return referenceEntrySet.size(); + } + }; + } + + @Override + public void forEach(final BiConsumer action) { + this.purgeStale(); + this.raw.forEach((key, rValue) -> action.accept(key.get(), unwrap(rValue))); + } + + @Override + public Iterator> iterator() { + return entrySet().iterator(); + } + + @Override + public V compute(final K key, final BiFunction remappingFunction) { + this.purgeStale(); + return unwrap(this.raw.compute(wrapKey(key), + (kReference, vReference) -> wrapValue(remappingFunction.apply(unwrap(kReference), unwrap(vReference))))); + } + + @Override + public V merge(final K key, final V value, final BiFunction remappingFunction) { + this.purgeStale(); + return unwrap(this.raw.merge(wrapKey(key), wrapValue(value), + (vReference, vReference2) -> wrapValue(remappingFunction.apply(unwrap(vReference), unwrap(vReference2))))); + } + + /** + * 清除被回收的键和值 + */ + @SuppressWarnings("unchecked") + private void purgeStale() { + Ref key; + Ref value; + + // 清除无效key对应键值对 + while ((key = (Ref) this.lastKeyQueue.poll()) != null) { + value = this.raw.remove(key); + if (null != purgeListener) { + purgeListener.accept(key, value); + } + } + + // 清除无效value对应的键值对 + while ((value = (Ref) this.lastValueQueue.poll()) != null) { + MapUtil.removeByValue(this.raw, (Ref) value); + if (null != purgeListener) { + purgeListener.accept(null, value); + } + } + } + + /** + * 根据Reference类型构建key对应的{@link Reference} + * + * @param key 键 + * @param queue {@link ReferenceQueue} + * @return {@link Reference} + */ + abstract Ref wrapKey(final K key, final ReferenceQueue queue); + + /** + * 根据Reference类型构建value对应的{@link Reference} + * + * @param value 值 + * @param queue {@link ReferenceQueue} + * @return {@link Reference} + */ + abstract Ref wrapValue(final V value, final ReferenceQueue queue); + + /** + * 根据Reference类型构建key对应的{@link Reference} + * + * @param key 键 + * @return {@link Reference} + */ + @SuppressWarnings("unchecked") + private Ref wrapKey(final Object key) { + return wrapKey((K) key, this.lastKeyQueue); + } + + /** + * 根据Reference类型构建value对应的{@link Reference} + * + * @param value 键 + * @return {@link Reference} + */ + @SuppressWarnings("unchecked") + private Ref wrapValue(final Object value) { + if(null == value){ + return (Ref) NullRef.NULL; + } + return wrapValue((V) value, this.lastValueQueue); + } + + /** + * 去包装对象 + * + * @param 对象类型 + * @param obj 对象 + * @return 值 + */ + private static T unwrap(final Ref obj) { + return ReferenceUtil.get(obj); + } + + @SuppressWarnings("rawtypes") + private static class NullRef implements Ref { + public static final Object NULL = new NullRef(); + + @Override + public Object get() { + return null; + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/reference/SoftConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/reference/SoftConcurrentMap.java new file mode 100644 index 000000000..582471637 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/reference/SoftConcurrentMap.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013-2025 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hutool.core.map.reference; + +import cn.hutool.core.lang.ref.Ref; +import cn.hutool.core.lang.ref.SoftObj; + +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 线程安全的SoftMap实现
+ * 键和值都为Soft引用,即,在GC报告内存不足时会被GC回收 + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.41 + */ +public class SoftConcurrentMap extends ReferenceConcurrentMap { + private static final long serialVersionUID = 1L; + + /** + * 构造 + */ + public SoftConcurrentMap() { + this(new ConcurrentHashMap<>()); + } + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + */ + public SoftConcurrentMap(final ConcurrentMap, Ref> raw) { + super(raw); + } + + @Override + Ref wrapKey(final K key, final ReferenceQueue queue) { + return new SoftObj<>(key, queue); + } + + @Override + Ref wrapValue(final V value, final ReferenceQueue queue) { + return new SoftObj<>(value, queue); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyConcurrentMap.java new file mode 100644 index 000000000..4565127e9 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyConcurrentMap.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013-2025 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hutool.core.map.reference; + +import cn.hutool.core.lang.ref.Ref; +import cn.hutool.core.lang.ref.StrongObj; +import cn.hutool.core.lang.ref.WeakObj; + +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 线程安全的WeakMap实现
+ * 键为Weak引用,即,在GC时发现弱引用会回收其对象 + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.41 + */ +public class WeakKeyConcurrentMap extends ReferenceConcurrentMap { + private static final long serialVersionUID = 1L; + + /** + * 构造 + */ + public WeakKeyConcurrentMap() { + this(new ConcurrentHashMap<>()); + } + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + */ + public WeakKeyConcurrentMap(final ConcurrentMap, Ref> raw) { + super(raw); + } + + @Override + Ref wrapKey(final K key, final ReferenceQueue queue) { + return new WeakObj<>(key, queue); + } + + @Override + Ref wrapValue(final V value, final ReferenceQueue queue) { + return new StrongObj<>(value); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyValueConcurrentMap.java b/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyValueConcurrentMap.java new file mode 100644 index 000000000..6f9b4a666 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/reference/WeakKeyValueConcurrentMap.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013-2025 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.hutool.core.map.reference; + +import cn.hutool.core.lang.ref.Ref; +import cn.hutool.core.lang.ref.WeakObj; + +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 线程安全的WeakMap实现
+ * 键和值都为Weak引用,即,在GC时发现弱引用会回收其对象 + * + * @param 键类型 + * @param 值类型 + * @author Looly + * @since 5.8.41 + */ +public class WeakKeyValueConcurrentMap extends ReferenceConcurrentMap { + private static final long serialVersionUID = 1L; + + /** + * 构造 + */ + public WeakKeyValueConcurrentMap() { + this(new ConcurrentHashMap<>()); + } + + /** + * 构造 + * + * @param raw {@link ConcurrentMap}实现 + */ + public WeakKeyValueConcurrentMap(final ConcurrentMap, Ref> raw) { + super(raw); + } + + @Override + Ref wrapKey(final K key, final ReferenceQueue queue) { + return new WeakObj<>(key, queue); + } + + @Override + Ref wrapValue(final V value, final ReferenceQueue queue) { + return new WeakObj<>(value, queue); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/map/reference/package-info.java b/hutool-core/src/main/java/cn/hutool/core/map/reference/package-info.java new file mode 100644 index 000000000..c4460587d --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/reference/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013-2025 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * 弱引用Map实现 + * + * @author Looly + * @since 5.8.41 + */ +package cn.hutool.core.map.reference; diff --git a/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java index 5cd56ca13..65fc07030 100755 --- a/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java +++ b/hutool-core/src/main/java/cn/hutool/core/net/RFC3986.java @@ -71,8 +71,6 @@ public class RFC3986 { /** * query中的value编码器,严格模式,value中不能包含任何分隔符。 - * - * @since 6.0.0 */ public static final PercentCodec QUERY_PARAM_VALUE_STRICT = UNRESERVED; @@ -84,8 +82,6 @@ public class RFC3986 { /** * query中的key编码器,严格模式,key中不能包含任何分隔符。 - * - * @since 6.0.0 */ public static final PercentCodec QUERY_PARAM_NAME_STRICT = UNRESERVED; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java index 02f7b48ee..e49cc85f5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ObjectUtil.java @@ -8,6 +8,7 @@ import cn.hutool.core.map.MapUtil; import java.lang.reflect.Array; import java.util.*; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -367,6 +368,33 @@ public class ObjectUtil { return defaultValue; } + /** + * 如果指定的对象不为 {@code null},则应用提供的映射函数并返回结果,否则返回 {@code null}。 + * + * @param source 要检查的对象 + * @param handler 要应用的映射函数 + * @param 输入对象的类型 + * @param 映射函数的返回类型 + * @return 映射函数的结果, 如果输入对象为 null,则返回 null + * @since 5.8.41 + */ + public static R apply(final T source, final Function handler) { + return defaultIfNull(source, handler, null); + } + + /** + * 如果指定的对象不为 {@code null},则执行{@link Consumer}处理source,否则不进行操作 + * + * @param source 要检查的对象 + * @param consumer source处理逻辑 + * @param 输入对象的类型 + */ + public static void accept(final T source, final Consumer consumer) { + if (null != source) { + consumer.accept(source); + } + } + /** * 如果给定对象为{@code null}或者""返回默认值, 否则返回自定义handle处理后的返回值 * diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReferenceUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReferenceUtil.java index e4b13b6a6..4f7bf36d3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReferenceUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReferenceUtil.java @@ -1,10 +1,8 @@ package cn.hutool.core.util; -import java.lang.ref.PhantomReference; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; +import cn.hutool.core.lang.ref.Ref; + +import java.lang.ref.*; /** * 引用工具类,主要针对{@link Reference} 工具化封装
@@ -54,6 +52,30 @@ public class ReferenceUtil { } } + /** + * {@code null}全的解包获取原始对象 + * + * @param 对象类型 + * @param obj Reference对象 + * @return 原始对象 or {@code null} + * @since 5.8.41 + */ + public static T get(final Reference obj) { + return ObjUtil.apply(obj, Reference::get); + } + + /** + * {@code null}安全的解包获取原始对象 + * + * @param 对象类型 + * @param obj Ref对象 + * @return 原始对象 or {@code null} + * @since 5.8.41 + */ + public static T get(final Ref obj) { + return ObjUtil.apply(obj, Ref::get); + } + /** * 引用类型 * diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index 8ba0feee0..3df08e42f 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -11,22 +11,10 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.reflect.MethodHandleUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.lang.reflect.*; +import java.util.*; /** * 反射工具类 @@ -39,15 +27,15 @@ public class ReflectUtil { /** * 构造对象缓存 */ - private static final WeakConcurrentMap, Constructor[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap, Constructor[]> CONSTRUCTORS_CACHE = new WeakKeyValueConcurrentMap<>(); /** * 字段缓存 */ - private static final WeakConcurrentMap, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap, Field[]> FIELDS_CACHE = new WeakKeyValueConcurrentMap<>(); /** * 方法缓存 */ - private static final WeakConcurrentMap, Method[]> METHODS_CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap, Method[]> METHODS_CACHE = new WeakKeyValueConcurrentMap<>(); // --------------------------------------------------------------------------------------------------------- Constructor @@ -89,7 +77,7 @@ public class ReflectUtil { @SuppressWarnings("unchecked") public static Constructor[] getConstructors(Class beanClass) throws SecurityException { Assert.notNull(beanClass); - return (Constructor[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, () -> getConstructorsDirectly(beanClass)); + return (Constructor[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, (key) -> getConstructorsDirectly(beanClass)); } /** @@ -178,7 +166,7 @@ public class ReflectUtil { */ public static Field[] getFields(Class beanClass) throws SecurityException { Assert.notNull(beanClass); - return FIELDS_CACHE.computeIfAbsent(beanClass, () -> getFieldsDirectly(beanClass, true)); + return FIELDS_CACHE.computeIfAbsent(beanClass, (key) -> getFieldsDirectly(beanClass, true)); } @@ -673,7 +661,7 @@ public class ReflectUtil { public static Method[] getMethods(Class beanClass) throws SecurityException { Assert.notNull(beanClass); return METHODS_CACHE.computeIfAbsent(beanClass, - () -> getMethodsDirectly(beanClass, true, true)); + (key) -> getMethodsDirectly(beanClass, true, true)); } /** 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 f4d384a61..f7be7ceb7 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 @@ -794,7 +794,6 @@ public class URLUtil extends URLEncodeUtil { * * @param url URL * @return 长度 - * @since 6.0.0 */ public static long size(final URL url) { if (URLUtil.isFileURL(url)) { @@ -826,7 +825,6 @@ public class URLUtil extends URLEncodeUtil { * 如果连接为JNLP方式,则打开缓存 * * @param con {@link URLConnection} - * @since 6.0.0 */ public static void useCachesIfNecessary(final URLConnection con) { con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); diff --git a/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java index 0efc5d863..32134664f 100755 --- a/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/map/WeakConcurrentMapTest.java @@ -1,17 +1,20 @@ package cn.hutool.core.map; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.thread.ConcurrencyTester; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class WeakConcurrentMapTest { @Test public void putAndGetTest(){ - final WeakConcurrentMap map = new WeakConcurrentMap<>(); + final WeakKeyValueConcurrentMap map = new WeakKeyValueConcurrentMap<>(); Object key1 = new Object(), value1 = new Object(), key2 = new Object(), value2 = new Object(), @@ -41,7 +44,7 @@ public class WeakConcurrentMapTest { @Test public void getConcurrencyTest(){ - final WeakConcurrentMap cache = new WeakConcurrentMap<>(); + final WeakKeyValueConcurrentMap cache = new WeakKeyValueConcurrentMap<>(); final ConcurrencyTester tester = new ConcurrencyTester(9000); tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue")); diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java index 8af979421..11529a9d3 100755 --- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java @@ -8,21 +8,18 @@ import cn.hutool.core.lang.Console; import cn.hutool.core.lang.test.bean.ExamInfoDict; import cn.hutool.core.util.ClassUtilTest.TestSubClass; import lombok.Data; -import static org.junit.jupiter.api.Assertions.*; +import lombok.EqualsAndHashCode; import lombok.experimental.FieldNameConstants; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.*; + /** * 反射工具类单元测试 * @@ -100,9 +97,9 @@ public class ReflectUtilTest { // 如果子类与父类中存在同名字段,则这两个字段同时存在,子类字段在前,父类字段在后。 fields = ReflectUtil.getFields(TestSubUser.class); assertEquals(4, fields.length); - List idFieldList = Arrays.asList(fields).stream().filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList()); + List idFieldList = Arrays.stream(fields).filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList()); Field firstIdField = CollUtil.getFirst(idFieldList); - assertEquals(true, Objects.equals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName())); + assertEquals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName()); } @Data @@ -111,6 +108,7 @@ public class ReflectUtilTest { private String remark; } + @EqualsAndHashCode(callSuper = true) @Data @FieldNameConstants static class TestSubUser extends TestBaseEntity { @@ -372,4 +370,5 @@ public class ReflectUtilTest { } } } + } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java b/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java index e40712d1c..772c7952a 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/cglib/BeanCopierCache.java @@ -1,6 +1,6 @@ package cn.hutool.extra.cglib; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.util.StrUtil; import net.sf.cglib.beans.BeanCopier; import net.sf.cglib.core.Converter; @@ -18,7 +18,7 @@ public enum BeanCopierCache { */ INSTANCE; - private final WeakConcurrentMap cache = new WeakConcurrentMap<>(); + private final WeakKeyValueConcurrentMap cache = new WeakKeyValueConcurrentMap<>(); /** * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/servlet/JakartaServletUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/servlet/JakartaServletUtil.java index 2d6e2a6ea..06a09fc06 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/servlet/JakartaServletUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/servlet/JakartaServletUtil.java @@ -308,7 +308,6 @@ public class JakartaServletUtil { * * @param request 请求对象{@link HttpServletRequest} * @return header值 - * @since 6.0.0 */ public static Map> getHeadersMap(final HttpServletRequest request) { final Map> headerMap = new LinkedHashMap<>(); diff --git a/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java index ecb3a88b0..73fde18d9 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java @@ -308,7 +308,6 @@ public class ServletUtil { * * @param request 请求对象{@link HttpServletRequest} * @return header值 - * @since 6.0.0 */ public static Map> getHeadersMap(final HttpServletRequest request) { final Map> headerMap = new LinkedHashMap<>(); diff --git a/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java b/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java index de674545c..0527d0d14 100755 --- a/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java +++ b/hutool-script/src/main/java/cn/hutool/script/ScriptUtil.java @@ -1,16 +1,9 @@ package cn.hutool.script; -import cn.hutool.core.map.WeakConcurrentMap; +import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap; import cn.hutool.core.util.StrUtil; -import javax.script.Bindings; -import javax.script.Compilable; -import javax.script.CompiledScript; -import javax.script.Invocable; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; +import javax.script.*; /** * 脚本工具类 @@ -20,7 +13,7 @@ import javax.script.ScriptException; public class ScriptUtil { private static final ScriptEngineManager MANAGER = new ScriptEngineManager(); - private static final WeakConcurrentMap CACHE = new WeakConcurrentMap<>(); + private static final WeakKeyValueConcurrentMap CACHE = new WeakKeyValueConcurrentMap<>(); /** * 获得单例的{@link ScriptEngine} 实例 @@ -29,7 +22,7 @@ public class ScriptUtil { * @return {@link ScriptEngine} 实例 */ public static ScriptEngine getScript(String nameOrExtOrMime) { - return CACHE.computeIfAbsent(nameOrExtOrMime, () -> createScript(nameOrExtOrMime)); + return CACHE.computeIfAbsent(nameOrExtOrMime, (key) -> createScript(nameOrExtOrMime)); } /**