Merge pull request #4098 from CherryRum/v5-dev

fix(core): 由于 JDK 24+ 修改,优化 ServiceLoader 加载第一个可用服务的逻辑
This commit is contained in:
Golden Looly 2025-10-11 19:41:55 +08:00 committed by GitHub
commit 1db0c23ba3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,9 +2,10 @@ package cn.hutool.core.util;
import cn.hutool.core.collection.ListUtil; import cn.hutool.core.collection.ListUtil;
import java.util.Iterator;
import java.util.List; import java.util.*;
import java.util.ServiceLoader;
/** /**
* SPI机制中的服务加载工具类流程如下 * SPI机制中的服务加载工具类流程如下
@ -22,20 +23,36 @@ import java.util.ServiceLoader;
public class ServiceLoaderUtil { public class ServiceLoaderUtil {
/** /**
* 加载第一个可用服务如果用户定义了多个接口实现类只获取第一个不报错的服务 * 加载第一个可用的 Service 实现
* <p>
* 为兼容 JDK 24+ {@link ServiceLoader} 在加载服务实现时可能抛出的 {@link NoClassDefFoundError}
* 此方法在调用 {@code hasNext()} {@code next()} 时安全忽略异常
* 当遇到依赖缺失或配置错误的实现时会自动跳过并返回第一个可用的非空实例
* </p>
* *
* @param <T> 接口类型 * @param clazz 服务接口类型
* @param clazz 服务接口 * @param <T> 服务实现类型
* @return 第一个服务接口实现对象无实现返回{@code null} * @return 第一个可用的非空实现若无可用实现则返回 {@code null}
* @since 5.8.41 JDK 24+ 兼容优化
* @see <a href="https://bugs.openjdk.org/browse/JDK-8350481">JDK-8350481</a>
*/ */
public static <T> T loadFirstAvailable(Class<T> clazz) { public static <T> T loadFirstAvailable(Class<T> clazz) {
final Iterator<T> iterator = load(clazz).iterator(); final ServiceLoader<T> loader = ServiceLoader.load(clazz);
while (iterator.hasNext()) { final Iterator<T> iterator = loader.iterator();
while (true) {
T instance;
try { try {
return iterator.next(); // 注意JDK 24+ hasNext() next() 均可能触发 NoClassDefFoundError
} catch (Throwable ignore) { if (!iterator.hasNext()) {
// issue#ID0952 JDK 25+ 会直接抛出 NoClassDefFoundError https://bugs.openjdk.org/browse/JDK-8350481 break;
// 这里安全忽略并尝试下一个实现 }
instance = iterator.next();
} catch (ServiceConfigurationError | NoClassDefFoundError e) {
// 安全忽略当前实现尝试下一个
continue;
}
if (instance != null) {
return instance;
} }
} }
return null; return null;