Merge remote-tracking branch 'origin/v5-dev' into v5-dev

This commit is contained in:
lpj
2025-09-01 19:04:48 +08:00
69 changed files with 1169 additions and 166 deletions

View File

@@ -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`增加构造方法支持BETWEENissue#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`方法原方法标记deprecatedissue#4047@Github
-------------------------------------------------------------------------------------------------------------
# 5.8.40(2025-08-26)

View File

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

View File

@@ -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平台没有测试不能保证所有工具类或工具方法可用。

View File

@@ -1 +1 @@
5.8.40
5.8.41

View File

@@ -1 +1 @@
var version = '5.8.40'
var version = '5.8.41'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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映射

View File

@@ -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映射

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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},找不到则编译正则表达式并入池。

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.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<>());
}
/**

View File

@@ -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实现类

View File

@@ -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) {

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

View File

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

View File

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

View File

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

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中不能包含任何分隔符。
*
* @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;

View File

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

View File

@@ -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处理后的返回值
*

View File

@@ -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);
}
/**
* 引用类型
*

View File

@@ -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));
}
/**

View File

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

View File

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

View File

@@ -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 {
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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));
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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));
}
/**

View File

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

View File

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

View File

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

View File

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