mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
This commit is contained in:
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,6 +1,22 @@
|
||||
|
||||
# 🚀Changelog
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.41(2025-08-30)
|
||||
|
||||
### 🐣新特性
|
||||
* 【core 】 增加`WeakKeyValueConcurrentMap`及其关联类,同时废弃`WeakConcurrentMap`并替换(issue#4039@Github)
|
||||
* 【core 】 `MapUtil`增加`removeByValue`和`removeIf`方法
|
||||
* 【core 】 `ObjectUtil`增加`apply`方法
|
||||
* 【core 】 `ReferenceUtil`增加`get`方法
|
||||
* 【db 】 `Condition`增加构造方法支持BETWEEN(issue#4041@Github)
|
||||
* 【core 】 `IoUtil.writeObjects`判空避免空指针(issue#4049@Github)
|
||||
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复`ReflectUtil`中因class和Method关联导致的缓存无法回收问题(issue#4039@Github)
|
||||
* 【db 】 修复`Condition`的`Condition("discount_end_time", "!=", (String) null)`方法生成SQL时,生成SQL不符合预期要求的错误(pr#4042@Github)
|
||||
* 【core 】 修复`IoUtil`的`closeIfPosible`拼写错误,新建一个`closeIfPossible`方法,原方法标记deprecated(issue#4047@Github)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
# 5.8.40(2025-08-26)
|
||||
|
||||
|
||||
@@ -134,18 +134,18 @@ Each module can be introduced individually, or all modules can be introduced by
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
```
|
||||
implementation 'cn.hutool:hutool-all:5.8.40'
|
||||
implementation 'cn.hutool:hutool-all:5.8.41'
|
||||
```
|
||||
|
||||
## 📥Download
|
||||
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.40/)
|
||||
- [Maven Repo](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.41/)
|
||||
|
||||
> 🔔️note:
|
||||
> Hutool 5.x supports JDK8+ and is not tested on Android platforms, and cannot guarantee that all tool classes or tool methods are available.
|
||||
|
||||
@@ -124,20 +124,20 @@ Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 🍐Gradle
|
||||
```
|
||||
implementation 'cn.hutool:hutool-all:5.8.40'
|
||||
implementation 'cn.hutool:hutool-all:5.8.41'
|
||||
```
|
||||
|
||||
### 📥下载jar
|
||||
|
||||
点击以下链接,下载`hutool-all-X.X.X.jar`即可:
|
||||
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.40/)
|
||||
- [Maven中央库](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.8.41/)
|
||||
|
||||
> 🔔️注意
|
||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。
|
||||
|
||||
@@ -1 +1 @@
|
||||
5.8.40
|
||||
5.8.41
|
||||
|
||||
@@ -1 +1 @@
|
||||
var version = '5.8.40'
|
||||
var version = '5.8.41'
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-ai</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-all</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-aop</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bloomFilter</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bom</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 弱引用缓存<br>
|
||||
@@ -27,16 +26,18 @@ public class WeakCache<K, V> extends TimedCache<K, V>{
|
||||
* @param timeout 超时时常,单位毫秒,-1或0表示无限制
|
||||
*/
|
||||
public WeakCache(long timeout) {
|
||||
super(timeout, new WeakConcurrentMap<>());
|
||||
super(timeout, new WeakKeyValueConcurrentMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public WeakCache<K, V> setListener(CacheListener<K, V> 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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-captcha</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-core</artifactId>
|
||||
|
||||
@@ -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属性缓存<br>
|
||||
@@ -12,7 +12,7 @@ import cn.hutool.core.map.WeakConcurrentMap;
|
||||
public enum BeanDescCache {
|
||||
INSTANCE;
|
||||
|
||||
private final WeakConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, BeanDesc> bdCache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link BeanDesc}Map映射
|
||||
|
||||
@@ -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<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakConcurrentMap<>();
|
||||
private final WeakConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> pdCache = new WeakKeyValueConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<Class<?>, Map<String, PropertyDescriptor>> ignoreCasePdCache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得属性名和{@link PropertyDescriptor}Map映射
|
||||
|
||||
@@ -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<Object> {
|
||||
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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.io.copy.ReaderWriterCopier;
|
||||
import cn.hutool.core.io.copy.StreamCopier;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -1071,6 +1072,10 @@ public class IoUtil extends NioUtil {
|
||||
public static void writeObjects(OutputStream out, boolean isCloseOut, Serializable... contents) throws IORuntimeException {
|
||||
ObjectOutputStream osw = null;
|
||||
try {
|
||||
if(ArrayUtil.isEmpty( contents)){
|
||||
return;
|
||||
}
|
||||
|
||||
osw = out instanceof ObjectOutputStream ? (ObjectOutputStream) out : new ObjectOutputStream(out);
|
||||
for (Object content : contents) {
|
||||
if (content != null) {
|
||||
@@ -1125,8 +1130,21 @@ public class IoUtil extends NioUtil {
|
||||
*
|
||||
* @param obj 可关闭对象
|
||||
* @since 4.3.2
|
||||
* @deprecated 拼写错误,请使用{@link #closeIfPossible(Object)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void closeIfPosible(Object obj) {
|
||||
closeIfPossible( obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试关闭指定对象<br>
|
||||
* 判断对象如果实现了{@link AutoCloseable},则调用之
|
||||
*
|
||||
* @param obj 可关闭对象
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public static void closeIfPossible(Object obj) {
|
||||
if (obj instanceof AutoCloseable) {
|
||||
close((AutoCloseable) obj);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<RegexWithFlag, Pattern> POOL = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<RegexWithFlag, Pattern> POOL = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 先从Pattern池中查找正则对应的{@link Pattern},找不到则编译正则表达式并入池。
|
||||
|
||||
@@ -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 <K> 键类型
|
||||
* @param <V> 值类型
|
||||
@@ -43,7 +43,7 @@ public class SimpleCache<K, V> implements Iterable<Map.Entry<K, V>>, Serializabl
|
||||
* 构造,默认使用{@link WeakHashMap}实现缓存自动清理
|
||||
*/
|
||||
public SimpleCache() {
|
||||
this(new WeakConcurrentMap<>());
|
||||
this(new WeakKeyValueConcurrentMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String, SerializedLambda> cache = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<String, SerializedLambda> cache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 通过对象的方法或类的静态方法引用,获取lambda实现类
|
||||
|
||||
@@ -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<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) {
|
||||
if (sample == null) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
18
hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java
Normal file
18
hutool-core/src/main/java/cn/hutool/core/lang/ref/Ref.java
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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<Type, Map<Type, Type>> CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<Type, Map<Type, Type>> CACHE = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获取泛型变量和泛型实际类型的对应关系Map
|
||||
|
||||
@@ -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中值为指定值的键值对<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
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
* @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 {
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.concurrent.ConcurrentMap;
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
* @since 5.8.0
|
||||
* @deprecated 请使用{@link cn.hutool.core.map.reference.WeakKeyConcurrentMap}
|
||||
*/
|
||||
public class WeakConcurrentMap<K, V> extends ReferenceConcurrentMap<K, V> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -75,14 +75,14 @@ public class DesensitizedUtil {
|
||||
* IPv6地址
|
||||
*/
|
||||
IPV6,
|
||||
/**
|
||||
* 护照号
|
||||
*/
|
||||
PASSPORT,
|
||||
/**
|
||||
* 统一社会信用代码
|
||||
*/
|
||||
CREDIT_CODE,
|
||||
/**
|
||||
* 护照号
|
||||
*/
|
||||
PASSPORT,
|
||||
/**
|
||||
* 统一社会信用代码
|
||||
*/
|
||||
CREDIT_CODE,
|
||||
/**
|
||||
* 定义了一个first_mask的规则,只显示第一个字符。
|
||||
*/
|
||||
@@ -161,12 +161,12 @@ public class DesensitizedUtil {
|
||||
case IPV6:
|
||||
newStr = ipv6(String.valueOf(str));
|
||||
break;
|
||||
case PASSPORT:
|
||||
newStr = passport(String.valueOf(str));
|
||||
break;
|
||||
case CREDIT_CODE:
|
||||
newStr = creditCode(String.valueOf(str));
|
||||
break;
|
||||
case PASSPORT:
|
||||
newStr = passport(String.valueOf(str));
|
||||
break;
|
||||
case CREDIT_CODE:
|
||||
newStr = creditCode(String.valueOf(str));
|
||||
break;
|
||||
case FIRST_MASK:
|
||||
newStr = firstMask(String.valueOf(str));
|
||||
break;
|
||||
@@ -412,29 +412,34 @@ public class DesensitizedUtil {
|
||||
return StrUtil.subBefore(ipv6, ':', false) + ":*:*:*:*:*:*:*";
|
||||
}
|
||||
|
||||
/**
|
||||
* 护照号脱敏
|
||||
* 规则:前2后2,长度不足时保留最小有效信息
|
||||
* 示例:PJ1234567 → PJ*****67
|
||||
*/
|
||||
public static String passport(String passport) {
|
||||
if (StrUtil.isBlank(passport)) return passport;
|
||||
final int length = passport.length();
|
||||
if (length <= 2) return StrUtil.hide(passport, 0, length);
|
||||
return StrUtil.hide(passport, 2, length - 2);
|
||||
}
|
||||
/**
|
||||
* 护照号脱敏
|
||||
* 规则:前2后2,长度不足时保留最小有效信息
|
||||
* 示例:PJ1234567 → PJ*****67
|
||||
*
|
||||
* @param passport 护照号
|
||||
* @return 脱敏后的护照号
|
||||
*/
|
||||
public static String passport(String passport) {
|
||||
if (StrUtil.isBlank(passport)) return passport;
|
||||
final int length = passport.length();
|
||||
if (length <= 2) return StrUtil.hide(passport, 0, length);
|
||||
return StrUtil.hide(passport, 2, length - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一社会信用代码脱敏
|
||||
* 规则:前4后4,长度不足时保留最小有效信息
|
||||
* 统一社会信用代码由18位数字或大写英文字母组成
|
||||
* 示例:91110108MA01ABCDE7 → 9111**********CDE7
|
||||
*
|
||||
*/
|
||||
public static String creditCode(String code) {
|
||||
if (StrUtil.isBlank(code)) return code;
|
||||
final int length = code.length();
|
||||
if (length <= 4) return StrUtil.hide(code, 0, length);
|
||||
return StrUtil.hide(code, 4, length - 4);
|
||||
}
|
||||
/**
|
||||
* 统一社会信用代码脱敏
|
||||
* 规则:前4后4,长度不足时保留最小有效信息
|
||||
* 统一社会信用代码由18位数字或大写英文字母组成
|
||||
* 示例:91110108MA01ABCDE7 → 9111**********CDE7
|
||||
*
|
||||
* @param code 统一社会信用代码
|
||||
* @return 脱敏后的统一社会信用代码
|
||||
*/
|
||||
public static String creditCode(String code) {
|
||||
if (StrUtil.isBlank(code)) return code;
|
||||
final int length = code.length();
|
||||
if (length <= 4) return StrUtil.hide(code, 0, length);
|
||||
return StrUtil.hide(code, 4, length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <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处理后的返回值
|
||||
*
|
||||
|
||||
@@ -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} 工具化封装<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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用类型
|
||||
*
|
||||
|
||||
@@ -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<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
|
||||
|
||||
@@ -89,7 +77,7 @@ public class ReflectUtil {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Constructor<T>[] getConstructors(Class<T> beanClass) throws SecurityException {
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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<Object, Object> map = new WeakConcurrentMap<>();
|
||||
final WeakKeyValueConcurrentMap<Object, Object> 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<String, String> cache = new WeakConcurrentMap<>();
|
||||
final WeakKeyValueConcurrentMap<String, String> cache = new WeakKeyValueConcurrentMap<>();
|
||||
final ConcurrencyTester tester = new ConcurrencyTester(9000);
|
||||
tester.test(()-> cache.computeIfAbsent("aaa" + RandomUtil.randomInt(2), (key)-> "aaaValue"));
|
||||
|
||||
|
||||
@@ -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<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);
|
||||
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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cron</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-db</artifactId>
|
||||
|
||||
@@ -127,7 +127,12 @@ public class Condition extends CloneSupport<Condition> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* 构造LIKE value条件,支持:
|
||||
* <ul>
|
||||
* <li>{@link LikeType#StartWith}:LIKE value%</li>
|
||||
* <li>{@link LikeType#EndWith}:LIKE %value</li>
|
||||
* <li>{@link LikeType#Contains}:LIKE %value%</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param field 字段
|
||||
* @param value 值
|
||||
@@ -138,6 +143,21 @@ public class Condition extends CloneSupport<Condition> {
|
||||
this.operator = OPERATOR_LIKE;
|
||||
this.value = SqlUtil.buildLikeValue(value, likeType, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造BETWEEN leftValue and rightValue条件
|
||||
*
|
||||
* @param field 字段
|
||||
* @param leftValue 左值
|
||||
* @param rightValue 右值
|
||||
* @since 5.8.41
|
||||
*/
|
||||
public Condition(final String field, final Object leftValue, final Object rightValue){
|
||||
this.field = field;
|
||||
this.operator = OPERATOR_BETWEEN;
|
||||
this.value = leftValue;
|
||||
this.secondValue = rightValue;
|
||||
}
|
||||
// --------------------------------------------------------------- Constructor end
|
||||
|
||||
// --------------------------------------------------------------- Getters and Setters start
|
||||
@@ -257,6 +277,17 @@ public class Condition extends CloneSupport<Condition> {
|
||||
return OPERATOR_IS.equalsIgnoreCase(this.operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否 IS NOT条件
|
||||
*
|
||||
* @return 是否IS NOT条件
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public boolean isOperatorIsNot() {
|
||||
return OPERATOR_IS_NOT.equalsIgnoreCase(this.operator);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否LIKE条件
|
||||
*
|
||||
@@ -268,18 +299,24 @@ public class Condition extends CloneSupport<Condition> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查值是否为null,如果为null转换为 "IS NULL"形式
|
||||
* 检查值是否为null,如果为null转换为 "IS NULL"或"IS NOT NULL"形式
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Condition checkValueNull() {
|
||||
if (null == this.value) {
|
||||
this.operator = OPERATOR_IS;
|
||||
// 检查是否为"不等于"操作符(包括!=和<>)
|
||||
if ("!=".equalsIgnoreCase(this.operator) || "<>".equalsIgnoreCase(this.operator)) {
|
||||
this.operator = OPERATOR_IS_NOT;
|
||||
} else {
|
||||
this.operator = OPERATOR_IS;
|
||||
}
|
||||
this.value = VALUE_NULL;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得between 类型中第二个值
|
||||
*
|
||||
@@ -345,8 +382,8 @@ public class Condition extends CloneSupport<Condition> {
|
||||
// 类似:" (?,?,?)" 或者 " (1,2,3,4)"
|
||||
buildValuePartForIN(conditionStrBuilder, paramValues);
|
||||
} else {
|
||||
if (isPlaceHolder() && false == isOperatorIs()) {
|
||||
// 使用条件表达式占位符,条件表达式并不适用于 IS NULL
|
||||
if (isPlaceHolder() && !isOperatorIs() && !isOperatorIsNot()) {
|
||||
// 使用条件表达式占位符,条件表达式并不适用于 IS NULL 和 IS NOT NULL
|
||||
conditionStrBuilder.append(" ?");
|
||||
if (null != paramValues) {
|
||||
paramValues.add(this.value);
|
||||
@@ -355,7 +392,7 @@ public class Condition extends CloneSupport<Condition> {
|
||||
// 直接使用条件值
|
||||
final String valueStr = String.valueOf(this.value);
|
||||
conditionStrBuilder.append(" ").append(isOperatorLike() ?
|
||||
StrUtil.wrap(valueStr, "'") : valueStr);
|
||||
StrUtil.wrap(valueStr, "'") : valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,18 @@ public class ConditionTest {
|
||||
Condition conditionNull = new Condition("user", null);
|
||||
assertEquals("user IS NULL", conditionNull.toString());
|
||||
|
||||
Condition conditionNull2 = new Condition("user", "=", (String) null);
|
||||
assertEquals("user IS NULL", conditionNull2.toString());
|
||||
|
||||
Condition conditionNotNull = new Condition("user", "!= null");
|
||||
assertEquals("user IS NOT NULL", conditionNotNull.toString());
|
||||
|
||||
Condition conditionNotNull2 = new Condition("user", "!=", (String) null);
|
||||
assertEquals("user IS NOT NULL", conditionNotNull2.toString());
|
||||
|
||||
Condition conditionNotNull3 = new Condition("user", "<>", (String) null);
|
||||
assertEquals("user IS NOT NULL", conditionNotNull3.toString());
|
||||
|
||||
Condition condition2 = new Condition("user", "= zhangsan");
|
||||
assertEquals("user = ?", condition2.toString());
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-dfa</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
|
||||
@@ -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<String, BeanCopier> cache = new WeakConcurrentMap<>();
|
||||
private final WeakKeyValueConcurrentMap<String, BeanCopier> cache = new WeakKeyValueConcurrentMap<>();
|
||||
|
||||
/**
|
||||
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素
|
||||
@@ -43,7 +43,7 @@ public enum BeanCopierCache {
|
||||
*/
|
||||
public BeanCopier get(Class<?> srcClass, Class<?> targetClass, boolean useConverter) {
|
||||
final String key = genKey(srcClass, targetClass, useConverter);
|
||||
return cache.computeIfAbsent(key, () -> BeanCopier.create(srcClass, targetClass, useConverter));
|
||||
return cache.computeIfAbsent(key, (k) -> BeanCopier.create(srcClass, targetClass, useConverter));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -308,7 +308,6 @@ public class JakartaServletUtil {
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @return header值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
|
||||
final Map<String, List<String>> headerMap = new LinkedHashMap<>();
|
||||
|
||||
@@ -308,7 +308,6 @@ public class ServletUtil {
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @return header值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Map<String, List<String>> getHeadersMap(final HttpServletRequest request) {
|
||||
final Map<String, List<String>> headerMap = new LinkedHashMap<>();
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-http</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-json</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-jwt</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-log</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-poi</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-script</artifactId>
|
||||
|
||||
@@ -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<String, ScriptEngine> CACHE = new WeakConcurrentMap<>();
|
||||
private static final WeakKeyValueConcurrentMap<String, ScriptEngine> 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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-setting</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-socket</artifactId>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-system</artifactId>
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -8,7 +8,7 @@
|
||||
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.40</version>
|
||||
<version>5.8.41-SNAPSHOT</version>
|
||||
<name>hutool</name>
|
||||
<description>Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。</description>
|
||||
<url>https://github.com/chinabugotech/hutool</url>
|
||||
|
||||
Reference in New Issue
Block a user