修复ReflectUtil中因class和Method关联导致的缓存无法回收问题(issue#4039@Github)

This commit is contained in:
Looly 2025-08-29 11:52:06 +08:00
parent c1c92def35
commit bd123386ce
38 changed files with 1015 additions and 91 deletions

View File

@ -2,10 +2,16 @@
# 🚀Changelog # 🚀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修复 ### 🐞Bug修复
* 【core 】 修复`ReflectUtil`中因class和Method关联导致的缓存无法回收问题issue#4039@Github
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.8.40(2025-08-26) # 5.8.40(2025-08-26)

View File

@ -3,9 +3,8 @@ package cn.hutool.cache.impl;
import cn.hutool.cache.CacheListener; import cn.hutool.cache.CacheListener;
import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.lang.ref.Ref;
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import java.lang.ref.Reference;
/** /**
* 弱引用缓存<br> * 弱引用缓存<br>
@ -27,16 +26,18 @@ public class WeakCache<K, V> extends TimedCache<K, V>{
* @param timeout 超时时常单位毫秒-1或0表示无限制 * @param timeout 超时时常单位毫秒-1或0表示无限制
*/ */
public WeakCache(long timeout) { public WeakCache(long timeout) {
super(timeout, new WeakConcurrentMap<>()); super(timeout, new WeakKeyValueConcurrentMap<>());
} }
@Override @Override
public WeakCache<K, V> setListener(CacheListener<K, V> listener) { public WeakCache<K, V> setListener(CacheListener<K, V> listener) {
super.setListener(listener); super.setListener(listener);
final WeakConcurrentMap<Mutable<K>, CacheObj<K, V>> map = (WeakConcurrentMap<Mutable<K>, CacheObj<K, V>>) this.cacheMap; final WeakKeyValueConcurrentMap<Mutable<K>, CacheObj<K, V>> map = (WeakKeyValueConcurrentMap<Mutable<K>, CacheObj<K, V>>) this.cacheMap;
// WeakKey回收之后key对应的值已经是null了因此此处的key也为null // 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; return this;
} }

View File

@ -1,7 +1,7 @@
package cn.hutool.core.bean; package cn.hutool.core.bean;
import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
/** /**
* Bean属性缓存<br> * Bean属性缓存<br>
@ -12,7 +12,7 @@ import cn.hutool.core.map.WeakConcurrentMap;
public enum BeanDescCache { public enum BeanDescCache {
INSTANCE; INSTANCE;
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>(); private final WeakKeyValueConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakKeyValueConcurrentMap<>();
/** /**
* 获得属性名和{@link BeanDesc}Map映射 * 获得属性名和{@link BeanDesc}Map映射

View File

@ -1,8 +1,8 @@
package cn.hutool.core.bean; package cn.hutool.core.bean;
import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.map.ReferenceConcurrentMap; import cn.hutool.core.map.reference.ReferenceConcurrentMap;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.util.Map; import java.util.Map;
@ -16,8 +16,8 @@ import java.util.Map;
public enum BeanInfoCache { public enum BeanInfoCache {
INSTANCE; INSTANCE;
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>(); private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakKeyValueConcurrentMap<>();
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>(); private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakKeyValueConcurrentMap<>();
/** /**
* 获得属性名和{@link PropertyDescriptor}Map映射 * 获得属性名和{@link PropertyDescriptor}Map映射

View File

@ -4,7 +4,7 @@ import cn.hutool.core.convert.AbstractConverter;
import cn.hutool.core.convert.ConvertException; import cn.hutool.core.convert.ConvertException;
import cn.hutool.core.lang.EnumItem; import cn.hutool.core.lang.EnumItem;
import cn.hutool.core.map.MapUtil; 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.ClassUtil;
import cn.hutool.core.util.EnumUtil; import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ModifierUtil; import cn.hutool.core.util.ModifierUtil;
@ -25,7 +25,7 @@ import java.util.stream.Collectors;
public class EnumConverter extends AbstractConverter<Object> { public class EnumConverter extends AbstractConverter<Object> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final WeakConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<Class<?>, Map<Class<?>, Method>> VALUE_OF_METHOD_CACHE = new WeakKeyValueConcurrentMap<>();
private final Class enumClass; private final Class enumClass;

View File

@ -204,7 +204,6 @@ public class ColorUtil {
* *
* @param color 指定颜色 * @param color 指定颜色
* @return 其余颜色与color的最大距离 * @return 其余颜色与color的最大距离
* @since 6.0.0-M16
*/ */
public static int maxDistance(final Color color) { public static int maxDistance(final Color color) {
if (null == color) { if (null == color) {

View File

@ -735,7 +735,6 @@ public class PathUtil {
* @param dir 临时文件创建的所在目录 * @param dir 临时文件创建的所在目录
* @return 临时文件 * @return 临时文件
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
* @since 6.0.0
*/ */
public static Path createTempFile(final String prefix, final String suffix, final Path dir) throws IORuntimeException { public static Path createTempFile(final String prefix, final String suffix, final Path dir) throws IORuntimeException {
int exceptionsCount = 0; int exceptionsCount = 0;

View File

@ -1,6 +1,6 @@
package cn.hutool.core.lang; package cn.hutool.core.lang;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -196,7 +196,7 @@ public class PatternPool {
/** /**
* Pattern池 * Pattern池
*/ */
private static final WeakConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakKeyValueConcurrentMap<>();
/** /**
* 先从Pattern池中查找正则对应的{@link Pattern}找不到则编译正则表达式并入池 * 先从Pattern池中查找正则对应的{@link Pattern}找不到则编译正则表达式并入池

View File

@ -6,7 +6,7 @@ import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.SafeConcurrentHashMap; 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.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
@ -19,7 +19,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* 简单缓存无超时实现默认使用{@link WeakConcurrentMap}实现缓存自动清理 * 简单缓存无超时实现默认使用{@link WeakKeyValueConcurrentMap}实现缓存自动清理
* *
* @param <K> 键类型 * @param <K> 键类型
* @param <V> 值类型 * @param <V> 值类型
@ -43,7 +43,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
* 构造默认使用{@link WeakHashMap}实现缓存自动清理 * 构造默认使用{@link WeakHashMap}实现缓存自动清理
*/ */
public SimpleCache() { public SimpleCache() {
this(new WeakConcurrentMap<>()); this(new WeakKeyValueConcurrentMap<>());
} }
/** /**

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.func; package cn.hutool.core.lang.func;
import cn.hutool.core.bean.BeanUtil; 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.ClassUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@ -18,7 +18,7 @@ import java.lang.invoke.SerializedLambda;
*/ */
public class LambdaUtil { public class LambdaUtil {
private static final WeakConcurrentMap<String, SerializedLambda> cache = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<String, SerializedLambda> cache = new WeakKeyValueConcurrentMap<>();
/** /**
* 通过对象的方法或类的静态方法引用获取lambda实现类 * 通过对象的方法或类的静态方法引用获取lambda实现类

View File

@ -1,6 +1,6 @@
package cn.hutool.core.lang.intern; package cn.hutool.core.lang.intern;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -12,7 +12,7 @@ import java.lang.ref.WeakReference;
*/ */
public class WeakInterner<T> implements Interner<T>{ public class WeakInterner<T> implements Interner<T>{
private final WeakConcurrentMap<T, WeakReference<T>> cache = new WeakConcurrentMap<>(); private final WeakKeyValueConcurrentMap<T, WeakReference<T>> cache = new WeakKeyValueConcurrentMap<>();
public T intern(T sample) { public T intern(T sample) {
if (sample == null) { if (sample == null) {

View File

@ -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} <br>
* 此时对象未被真正回收要等到{@link ReferenceQueue}被真正处理后才会被回收
*
* @param <T> 键类型
*/
public class PhantomObj<T> extends PhantomReference<T> implements Ref<T>{
private final int hashCode;
/**
* 构造
*
* @param obj 原始对象
* @param queue {@link ReferenceQueue}
*/
public PhantomObj(final T obj, final ReferenceQueue<? super T> 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;
}
}

View File

@ -0,0 +1,18 @@
package cn.hutool.core.lang.ref;
/**
* 针对{@link java.lang.ref.Reference}的接口定义用于扩展功能<br>
* 例如提供自定义的无需回收对象
*
* @param <T> 对象类型
*/
@FunctionalInterface
public interface Ref<T> {
/**
* 获取引用的原始对象
*
* @return 原始对象
*/
T get();
}

View File

@ -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} <br>
* 此时对象未被真正回收要等到{@link ReferenceQueue}被真正处理后才会被回收
*/
PHANTOM
}

View File

@ -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 <T> 键类型
*/
public class SoftObj<T> extends SoftReference<T> implements Ref<T>{
private final int hashCode;
/**
* 构造
*
* @param obj 原始对象
* @param queue {@link ReferenceQueue}
*/
public SoftObj(final T obj, final ReferenceQueue<? super T> 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;
}
}

View File

@ -0,0 +1,42 @@
package cn.hutool.core.lang.ref;
import java.util.Objects;
/**
* 弱引用对象在GC时发现弱引用会回收其对象
*
* @param <T> 键类型
*/
public class StrongObj<T> implements Ref<T> {
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;
}
}

View File

@ -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 <T> 键类型
*/
public class WeakObj<T> extends WeakReference<T> implements Ref<T>{
private final int hashCode;
/**
* 构造
*
* @param obj 原始对象
* @param queue {@link ReferenceQueue}
*/
public WeakObj(final T obj, final ReferenceQueue<? super T> 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;
}
}

View File

@ -0,0 +1,10 @@
/**
* 引用工具封装主要针对{@link java.lang.ref.Reference} 工具化封装<br>
* 主要封装包括
* <pre>
* 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}被真正处理后才会被回收
* </pre>
*/
package cn.hutool.core.lang.ref;

View File

@ -1,7 +1,7 @@
package cn.hutool.core.lang.reflect; package cn.hutool.core.lang.reflect;
import cn.hutool.core.convert.Convert; 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.ArrayUtil;
import cn.hutool.core.util.TypeUtil; import cn.hutool.core.util.TypeUtil;
@ -20,7 +20,7 @@ import java.util.Map;
*/ */
public class ActualTypeMapperPool { public class ActualTypeMapperPool {
private static final WeakConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakKeyValueConcurrentMap<>();
/** /**
* 获取泛型变量和泛型实际类型的对应关系Map * 获取泛型变量和泛型实际类型的对应关系Map

View File

@ -5,16 +5,14 @@ import cn.hutool.core.convert.Convert;
import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.*; import cn.hutool.core.lang.*;
import cn.hutool.core.stream.CollectorUtil; import cn.hutool.core.stream.CollectorUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.*;
import cn.hutool.core.util.JdkUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
/** /**
* Map相关工具类 * Map相关工具类
@ -1347,6 +1345,40 @@ public class MapUtil {
return map; return map;
} }
/**
* 去除Map中值为指定值的键值对<br>
* 注意此方法在传入的Map上直接修改
*
* @param <K> key的类型
* @param <V> value的类型
* @param map Map
* @param value 给定值
* @return map
* @since 5.8.41
*/
public static <K, V> Map<K, V> removeByValue(final Map<K, V> map, final V value) {
return removeIf(map, entry -> ObjUtil.equals(value, entry.getValue()));
}
/**
* 去除Map中值为{@code null}的键值对<br>
* 注意此方法在传入的Map上直接修改
*
* @param <K> key的类型
* @param <V> value的类型
* @param map Map
* @param predicate 移除条件{@link Predicate#test(Object)}{@code true}时移除
* @return map
* @since 5.8.41
*/
public static <K, V> Map<K, V> removeIf(final Map<K, V> map, final Predicate<Entry<K, V>> predicate) {
if (isEmpty(map)) {
return map;
}
map.entrySet().removeIf(predicate);
return map;
}
/** /**
* 返回一个空Map * 返回一个空Map
* *

View File

@ -30,6 +30,7 @@ import java.util.stream.Collectors;
* @param <V> 值类型 * @param <V> 值类型
* @author looly * @author looly
* @since 5.8.0 * @since 5.8.0
* @deprecated 请使用{@link cn.hutool.core.map.reference.ReferenceConcurrentMap}
*/ */
public class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable { public class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {

View File

@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentMap;
* @param <V> 值类型 * @param <V> 值类型
* @author looly * @author looly
* @since 5.8.0 * @since 5.8.0
* @deprecated 请使用{@link cn.hutool.core.map.reference.WeakKeyConcurrentMap}
*/ */
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> { public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {

View File

@ -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 <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.41
*/
public abstract class ReferenceConcurrentMap<K, V> implements ConcurrentMap<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
private static final long serialVersionUID = 1L;
/**
* 键值对引用
*/
final ConcurrentMap<Ref<K>, Ref<V>> raw;
/**
* 因无强链接而清除的键队列
*/
private final ReferenceQueue<K> lastKeyQueue;
/**
* 因无强链接而清除的值队列
*/
private final ReferenceQueue<V> lastValueQueue;
/**
* 回收监听
*/
private BiConsumer<Ref<? extends K>, Ref<? extends V>> purgeListener;
// region 构造
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public ReferenceConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
this.raw = raw;
lastKeyQueue = new ReferenceQueue<>();
lastValueQueue = new ReferenceQueue<>();
}
// endregion
/**
* 设置对象回收清除监听
*
* @param purgeListener 监听函数
*/
public void setPurgeListener(final BiConsumer<Ref<? extends K>, Ref<? extends V>> 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<V> 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<V> vReference = this.raw.putIfAbsent(wrapKey(key), wrapValue(value));
return unwrap(vReference);
}
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public V replace(final K key, final V value) {
this.purgeStale();
final Ref<V> 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<? super K, ? super V, ? extends V> function) {
this.purgeStale();
this.raw.replaceAll((rKey, rValue) -> wrapValue(function.apply(unwrap(rKey), unwrap(rValue))));
}
@Override
public V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
V result = null;
while(null == result){
this.purgeStale();
final Ref<V> 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<? super K, ? super V, ? extends V> remappingFunction) {
V result = null;
while(null == result){
this.purgeStale();
final Ref<V> 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<K> keySet() {
this.purgeStale();
final Set<Ref<K>> referenceSet = this.raw.keySet();
return new AbstractSet<K>() {
@Override
public Iterator<K> iterator() {
final Iterator<Ref<K>> referenceIter = referenceSet.iterator();
return new Iterator<K>() {
@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<V> values() {
this.purgeStale();
final Collection<Ref<V>> referenceValues = this.raw.values();
return new AbstractCollection<V>() {
@Override
public Iterator<V> iterator() {
final Iterator<Ref<V>> referenceIter = referenceValues.iterator();
return new Iterator<V>() {
@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<Entry<K, V>> entrySet() {
this.purgeStale();
final Set<Entry<Ref<K>, Ref<V>>> referenceEntrySet = this.raw.entrySet();
return new AbstractSet<Entry<K, V>>() {
@Override
public Iterator<Entry<K, V>> iterator() {
final Iterator<Entry<Ref<K>, Ref<V>>> referenceIter = referenceEntrySet.iterator();
return new Iterator<Entry<K, V>>() {
@Override
public boolean hasNext() {
return referenceIter.hasNext();
}
@Override
public Entry<K, V> next() {
final Entry<Ref<K>, Ref<V>> next = referenceIter.next();
return new Entry<K, V>() {
@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<? super K, ? super V> action) {
this.purgeStale();
this.raw.forEach((key, rValue) -> action.accept(key.get(), unwrap(rValue)));
}
@Override
public Iterator<Entry<K, V>> iterator() {
return entrySet().iterator();
}
@Override
public V compute(final K key, final BiFunction<? super K, ? super V, ? extends V> 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<? super V, ? super V, ? extends V> 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<? extends K> key;
Ref<? extends V> value;
// 清除无效key对应键值对
while ((key = (Ref<? extends K>) this.lastKeyQueue.poll()) != null) {
value = this.raw.remove(key);
if (null != purgeListener) {
purgeListener.accept(key, value);
}
}
// 清除无效value对应的键值对
while ((value = (Ref<? extends V>) this.lastValueQueue.poll()) != null) {
MapUtil.removeByValue(this.raw, (Ref<V>) value);
if (null != purgeListener) {
purgeListener.accept(null, value);
}
}
}
/**
* 根据Reference类型构建key对应的{@link Reference}
*
* @param key
* @param queue {@link ReferenceQueue}
* @return {@link Reference}
*/
abstract Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue);
/**
* 根据Reference类型构建value对应的{@link Reference}
*
* @param value
* @param queue {@link ReferenceQueue}
* @return {@link Reference}
*/
abstract Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue);
/**
* 根据Reference类型构建key对应的{@link Reference}
*
* @param key
* @return {@link Reference}
*/
@SuppressWarnings("unchecked")
private Ref<K> wrapKey(final Object key) {
return wrapKey((K) key, this.lastKeyQueue);
}
/**
* 根据Reference类型构建value对应的{@link Reference}
*
* @param value
* @return {@link Reference}
*/
@SuppressWarnings("unchecked")
private Ref<V> wrapValue(final Object value) {
if(null == value){
return (Ref<V>) NullRef.NULL;
}
return wrapValue((V) value, this.lastValueQueue);
}
/**
* 去包装对象
*
* @param <T> 对象类型
* @param obj 对象
* @return
*/
private static <T> T unwrap(final Ref<T> 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;
}
}
}

View File

@ -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实现<br>
* 键和值都为Soft引用在GC报告内存不足时会被GC回收
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.41
*/
public class SoftConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* 构造
*/
public SoftConcurrentMap() {
this(new ConcurrentHashMap<>());
}
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public SoftConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
super(raw);
}
@Override
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
return new SoftObj<>(key, queue);
}
@Override
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
return new SoftObj<>(value, queue);
}
}

View File

@ -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实现<br>
* 键为Weak引用在GC时发现弱引用会回收其对象
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.41
*/
public class WeakKeyConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* 构造
*/
public WeakKeyConcurrentMap() {
this(new ConcurrentHashMap<>());
}
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public WeakKeyConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
super(raw);
}
@Override
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
return new WeakObj<>(key, queue);
}
@Override
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
return new StrongObj<>(value);
}
}

View File

@ -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实现<br>
* 键和值都为Weak引用在GC时发现弱引用会回收其对象
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
* @since 5.8.41
*/
public class WeakKeyValueConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* 构造
*/
public WeakKeyValueConcurrentMap() {
this(new ConcurrentHashMap<>());
}
/**
* 构造
*
* @param raw {@link ConcurrentMap}实现
*/
public WeakKeyValueConcurrentMap(final ConcurrentMap<Ref<K>, Ref<V>> raw) {
super(raw);
}
@Override
Ref<K> wrapKey(final K key, final ReferenceQueue<? super K> queue) {
return new WeakObj<>(key, queue);
}
@Override
Ref<V> wrapValue(final V value, final ReferenceQueue<? super V> queue) {
return new WeakObj<>(value, queue);
}
}

View File

@ -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;

View File

@ -71,8 +71,6 @@ public class RFC3986 {
/** /**
* query中的value编码器严格模式value中不能包含任何分隔符 * query中的value编码器严格模式value中不能包含任何分隔符
*
* @since 6.0.0
*/ */
public static final PercentCodec QUERY_PARAM_VALUE_STRICT = UNRESERVED; public static final PercentCodec QUERY_PARAM_VALUE_STRICT = UNRESERVED;
@ -84,8 +82,6 @@ public class RFC3986 {
/** /**
* query中的key编码器严格模式key中不能包含任何分隔符 * query中的key编码器严格模式key中不能包含任何分隔符
*
* @since 6.0.0
*/ */
public static final PercentCodec QUERY_PARAM_NAME_STRICT = UNRESERVED; public static final PercentCodec QUERY_PARAM_NAME_STRICT = UNRESERVED;

View File

@ -8,6 +8,7 @@ import cn.hutool.core.map.MapUtil;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -367,6 +368,33 @@ public class ObjectUtil {
return defaultValue; return defaultValue;
} }
/**
* 如果指定的对象不为 {@code null},则应用提供的映射函数并返回结果,否则返回 {@code null}
*
* @param source 要检查的对象
* @param handler 要应用的映射函数
* @param <T> 输入对象的类型
* @param <R> 映射函数的返回类型
* @return 映射函数的结果, 如果输入对象为 null,则返回 null
* @since 5.8.41
*/
public static <T, R> R apply(final T source, final Function<T, R> handler) {
return defaultIfNull(source, handler, null);
}
/**
* 如果指定的对象不为 {@code null},则执行{@link Consumer}处理source否则不进行操作
*
* @param source 要检查的对象
* @param consumer source处理逻辑
* @param <T> 输入对象的类型
*/
public static <T> void accept(final T source, final Consumer<T> consumer) {
if (null != source) {
consumer.accept(source);
}
}
/** /**
* 如果给定对象为{@code null}或者""返回默认值, 否则返回自定义handle处理后的返回值 * 如果给定对象为{@code null}或者""返回默认值, 否则返回自定义handle处理后的返回值
* *

View File

@ -1,10 +1,8 @@
package cn.hutool.core.util; package cn.hutool.core.util;
import java.lang.ref.PhantomReference; import cn.hutool.core.lang.ref.Ref;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.*;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
/** /**
* 引用工具类主要针对{@link Reference} 工具化封装<br> * 引用工具类主要针对{@link Reference} 工具化封装<br>
@ -54,6 +52,30 @@ public class ReferenceUtil {
} }
} }
/**
* {@code null}全的解包获取原始对象
*
* @param <T> 对象类型
* @param obj Reference对象
* @return 原始对象 or {@code null}
* @since 5.8.41
*/
public static <T> T get(final Reference<T> obj) {
return ObjUtil.apply(obj, Reference::get);
}
/**
* {@code null}安全的解包获取原始对象
*
* @param <T> 对象类型
* @param obj Ref对象
* @return 原始对象 or {@code null}
* @since 5.8.41
*/
public static <T> T get(final Ref<T> obj) {
return ObjUtil.apply(obj, Ref::get);
}
/** /**
* 引用类型 * 引用类型
* *

View File

@ -11,22 +11,10 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter; import cn.hutool.core.lang.Filter;
import cn.hutool.core.lang.reflect.MethodHandleUtil; import cn.hutool.core.lang.reflect.MethodHandleUtil;
import cn.hutool.core.map.MapUtil; 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.*;
import java.lang.reflect.Array; import java.util.*;
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;
/** /**
* 反射工具类 * 反射工具类
@ -39,15 +27,15 @@ public class ReflectUtil {
/** /**
* 构造对象缓存 * 构造对象缓存
*/ */
private static final WeakConcurrentMap<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new WeakKeyValueConcurrentMap<>();
/** /**
* 字段缓存 * 字段缓存
*/ */
private static final WeakConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<Class<?>, Field[]> FIELDS_CACHE = new WeakKeyValueConcurrentMap<>();
/** /**
* 方法缓存 * 方法缓存
*/ */
private static final WeakConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<Class<?>, Method[]> METHODS_CACHE = new WeakKeyValueConcurrentMap<>();
// --------------------------------------------------------------------------------------------------------- Constructor // --------------------------------------------------------------------------------------------------------- Constructor
@ -89,7 +77,7 @@ public class ReflectUtil {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> Constructor<T>[] getConstructors(Class<T> beanClass) throws SecurityException { public static <T> Constructor<T>[] getConstructors(Class<T> beanClass) throws SecurityException {
Assert.notNull(beanClass); Assert.notNull(beanClass);
return (Constructor<T>[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, () -> getConstructorsDirectly(beanClass)); return (Constructor<T>[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, (key) -> getConstructorsDirectly(beanClass));
} }
/** /**
@ -178,7 +166,7 @@ public class ReflectUtil {
*/ */
public static Field[] getFields(Class<?> beanClass) throws SecurityException { public static Field[] getFields(Class<?> beanClass) throws SecurityException {
Assert.notNull(beanClass); 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 { public static Method[] getMethods(Class<?> beanClass) throws SecurityException {
Assert.notNull(beanClass); Assert.notNull(beanClass);
return METHODS_CACHE.computeIfAbsent(beanClass, return METHODS_CACHE.computeIfAbsent(beanClass,
() -> getMethodsDirectly(beanClass, true, true)); (key) -> getMethodsDirectly(beanClass, true, true));
} }
/** /**

View File

@ -794,7 +794,6 @@ public class URLUtil extends URLEncodeUtil {
* *
* @param url URL * @param url URL
* @return 长度 * @return 长度
* @since 6.0.0
*/ */
public static long size(final URL url) { public static long size(final URL url) {
if (URLUtil.isFileURL(url)) { if (URLUtil.isFileURL(url)) {
@ -826,7 +825,6 @@ public class URLUtil extends URLEncodeUtil {
* 如果连接为JNLP方式则打开缓存 * 如果连接为JNLP方式则打开缓存
* *
* @param con {@link URLConnection} * @param con {@link URLConnection}
* @since 6.0.0
*/ */
public static void useCachesIfNecessary(final URLConnection con) { public static void useCachesIfNecessary(final URLConnection con) {
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));

View File

@ -1,17 +1,20 @@
package cn.hutool.core.map; package cn.hutool.core.map;
import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import cn.hutool.core.thread.ConcurrencyTester; import cn.hutool.core.thread.ConcurrencyTester;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test; 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 { public class WeakConcurrentMapTest {
@Test @Test
public void putAndGetTest(){ public void putAndGetTest(){
final WeakConcurrentMap<Object, Object> map = new WeakConcurrentMap<>(); final WeakKeyValueConcurrentMap<Object, Object> map = new WeakKeyValueConcurrentMap<>();
Object Object
key1 = new Object(), value1 = new Object(), key1 = new Object(), value1 = new Object(),
key2 = new Object(), value2 = new Object(), key2 = new Object(), value2 = new Object(),
@ -41,7 +44,7 @@ public class WeakConcurrentMapTest {
@Test @Test
public void getConcurrencyTest(){ public void getConcurrencyTest(){
final WeakConcurrentMap<String, String> cache = new WeakConcurrentMap<>(); final WeakKeyValueConcurrentMap<String, String> cache = new WeakKeyValueConcurrentMap<>();
final ConcurrencyTester tester = new ConcurrencyTester(9000); final ConcurrencyTester tester = new ConcurrencyTester(9000);
tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue")); tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue"));

View File

@ -8,21 +8,18 @@ import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.test.bean.ExamInfoDict; import cn.hutool.core.lang.test.bean.ExamInfoDict;
import cn.hutool.core.util.ClassUtilTest.TestSubClass; import cn.hutool.core.util.ClassUtilTest.TestSubClass;
import lombok.Data; import lombok.Data;
import static org.junit.jupiter.api.Assertions.*; import lombok.EqualsAndHashCode;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.*;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
/** /**
* 反射工具类单元测试 * 反射工具类单元测试
* *
@ -100,9 +97,9 @@ public class ReflectUtilTest {
// 如果子类与父类中存在同名字段则这两个字段同时存在子类字段在前父类字段在后 // 如果子类与父类中存在同名字段则这两个字段同时存在子类字段在前父类字段在后
fields = ReflectUtil.getFields(TestSubUser.class); fields = ReflectUtil.getFields(TestSubUser.class);
assertEquals(4, fields.length); assertEquals(4, fields.length);
List<Field> idFieldList = Arrays.asList(fields).stream().filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList()); List<Field> idFieldList = Arrays.stream(fields).filter(f -> Objects.equals(f.getName(), TestSubUser.Fields.id)).collect(Collectors.toList());
Field firstIdField = CollUtil.getFirst(idFieldList); Field firstIdField = CollUtil.getFirst(idFieldList);
assertEquals(true, Objects.equals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName())); assertEquals(firstIdField.getDeclaringClass().getName(), TestSubUser.class.getName());
} }
@Data @Data
@ -111,6 +108,7 @@ public class ReflectUtilTest {
private String remark; private String remark;
} }
@EqualsAndHashCode(callSuper = true)
@Data @Data
@FieldNameConstants @FieldNameConstants
static class TestSubUser extends TestBaseEntity { static class TestSubUser extends TestBaseEntity {
@ -372,4 +370,5 @@ public class ReflectUtilTest {
} }
} }
} }
} }

View File

@ -1,6 +1,6 @@
package cn.hutool.extra.cglib; 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 cn.hutool.core.util.StrUtil;
import net.sf.cglib.beans.BeanCopier; import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter; import net.sf.cglib.core.Converter;
@ -18,7 +18,7 @@ public enum BeanCopierCache {
*/ */
INSTANCE; INSTANCE;
private final WeakConcurrentMap<String, BeanCopier> cache = new WeakConcurrentMap<>(); private final WeakKeyValueConcurrentMap<String, BeanCopier> cache = new WeakKeyValueConcurrentMap<>();
/** /**
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素 * 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素

View File

@ -308,7 +308,6 @@ public class JakartaServletUtil {
* *
* @param request 请求对象{@link HttpServletRequest} * @param request 请求对象{@link HttpServletRequest}
* @return header值 * @return header值
* @since 6.0.0
*/ */
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) { public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
final Map<String, List<String>> headerMap = new LinkedHashMap<>(); final Map<String, List<String>> headerMap = new LinkedHashMap<>();

View File

@ -308,7 +308,6 @@ public class ServletUtil {
* *
* @param request 请求对象{@link HttpServletRequest} * @param request 请求对象{@link HttpServletRequest}
* @return header值 * @return header值
* @since 6.0.0
*/ */
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) { public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
final Map<String, List<String>> headerMap = new LinkedHashMap<>(); final Map<String, List<String>> headerMap = new LinkedHashMap<>();

View File

@ -1,16 +1,9 @@
package cn.hutool.script; package cn.hutool.script;
import cn.hutool.core.map.WeakConcurrentMap; import cn.hutool.core.map.reference.WeakKeyValueConcurrentMap;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import javax.script.Bindings; import javax.script.*;
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;
/** /**
* 脚本工具类 * 脚本工具类
@ -20,7 +13,7 @@ import javax.script.ScriptException;
public class ScriptUtil { public class ScriptUtil {
private static final ScriptEngineManager MANAGER = new ScriptEngineManager(); private static final ScriptEngineManager MANAGER = new ScriptEngineManager();
private static final WeakConcurrentMap<String, ScriptEngine> CACHE = new WeakConcurrentMap<>(); private static final WeakKeyValueConcurrentMap<String, ScriptEngine> CACHE = new WeakKeyValueConcurrentMap<>();
/** /**
* 获得单例的{@link ScriptEngine} 实例 * 获得单例的{@link ScriptEngine} 实例
@ -29,7 +22,7 @@ public class ScriptUtil {
* @return {@link ScriptEngine} 实例 * @return {@link ScriptEngine} 实例
*/ */
public static ScriptEngine getScript(String nameOrExtOrMime) { public static ScriptEngine getScript(String nameOrExtOrMime) {
return CACHE.computeIfAbsent(nameOrExtOrMime, () -> createScript(nameOrExtOrMime)); return CACHE.computeIfAbsent(nameOrExtOrMime, (key) -> createScript(nameOrExtOrMime));
} }
/** /**