You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效缓存Java ServiceLoader.Provider

缓存ServiceLoader.Provider实现预计算式类型安全获取

核心方案:安全的Unchecked强制转换

通过在缓存初始化阶段确保类型一致性,直接对缓存的List进行unchecked转换,避免每次调用时的遍历复制,满足"预计算好值随时可用"的需求。

1. 缓存初始化逻辑

首次加载服务时,直接获取对应泛型类型的Provider列表,再存入Map(这里的类型转换是安全的,因为我们明确知道列表元素的类型):

private static final Map<Class<?>, List<ServiceLoader.Provider<?>>> servicesProvidersMap = new ConcurrentHashMap<>();

public static <T> void initProviders(Class<T> clazz) {
    servicesProvidersMap.computeIfAbsent(clazz, key -> {
        // 加载对应类型的Provider列表,泛型明确为T
        List<ServiceLoader.Provider<T>> typedProviders = ServiceLoader.load(clazz)
                .stream()
                .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
        // 两次转换绕过编译器检查,本质是安全的
        return (List<ServiceLoader.Provider<?>>) (List<?>) typedProviders;
    });
}

2. 类型安全的获取方法

利用@SuppressWarnings("unchecked")抑制编译警告,直接返回转换后的列表:

@SuppressWarnings("unchecked")
public static <T> List<ServiceLoader.Provider<T>> getProvidersOf(Class<T> clazz) {
    // 转换安全:存入时已保证该Class对应的List是ServiceLoader.Provider<T>类型
    return (List<ServiceLoader.Provider<T>>) (List<?>) servicesProvidersMap.get(clazz);
}

为什么这个方案可行且安全

  • 缓存初始化时,通过ServiceLoader.load(clazz)获取的Provider<T>必然符合泛型约束,存入Map的转换只是绕开编译器的泛型擦除检查,运行时类型完全匹配。
  • 无需每次调用时复制或转换列表,完全满足"预计算、直接取用"的需求。

关于ServiceLoader.Provider长期缓存的说明

虽然ServiceLoader.Provider被设计为过渡对象,但它仅持有服务的加载元数据(如模块引用、构造器句柄),只要服务所在模块未被JVM卸载,缓存的Provider实例可以安全长期保存,调用get()依然能生成新的服务实例。


内容的提问来源于stack exchange,提问作者G. B. Wanscher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 09:01:25