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

Java ServiceLoader包装类失效:调用类识别错误问题

解决ServiceLoader调用者模块未声明uses的问题

方案1:使用预声明uses的代理模块

构建一个专门的代理模块,在其module-info.java中声明所有你需要加载的服务类型的uses语句,把这个模块加入到自定义ModuleLayer中,然后通过该模块内的工具类发起ServiceLoader调用——这样调用者模块就是代理模块,它已经满足uses声明要求。

示例:

  1. 代理模块的module-info.java:
module plugin.proxy {
    uses com.yourproject.services.ServiceA;
    uses com.yourproject.services.ServiceB;
    // 按需添加所有需要加载的服务类型
}
  1. 代理模块内的工具类:
package com.yourproject.proxy;

import java.util.ServiceLoader;

public class PluginServiceLoader {
    public static <S> ServiceLoader<S> load(ModuleLayer layer, Class<S> serviceType) {
        // 这里的调用者是PluginServiceLoader所在的plugin.proxy模块,已声明uses
        return ServiceLoader.load(layer, serviceType);
    }
}
  1. 在你的PluginLoader中调用:
public <S> ServiceLoader<S> loadServices(ModuleLayer layer, Class<S> serviceType) {
    return PluginServiceLoader.load(layer, serviceType);
}

优点:代码简洁,复用性强,无需反射;缺点:需要提前知道所有服务类型,无法适配完全动态的服务场景。

方案2:手动遍历模块收集服务提供者

绕开ServiceLoader的调用者检查逻辑,直接遍历目标ModuleLayer中的所有模块,手动提取服务提供者实例。这种方式不依赖调用者模块的uses声明,完全自主控制服务加载流程。

示例代码:

public <S> List<S> loadServices(ModuleLayer layer, Class<S> serviceType) {
    List<S> serviceInstances = new ArrayList<>();
    for (Module module : layer.modules()) {
        // 遍历模块中所有该服务的提供者
        module.findServiceProviders(serviceType)
              .forEach(provider -> {
                  try {
                      serviceInstances.add(provider.get());
                  } catch (Exception e) {
                      // 根据业务需求处理实例化异常,比如打印日志或跳过
                      e.printStackTrace();
                  }
              });
    }
    return serviceInstances;
}

优点:完全适配动态服务场景,无需额外模块;缺点:需要自行处理服务加载的异常、缓存等逻辑,不如ServiceLoader封装完善。

方案3:利用插件模块自身作为调用者

如果你的插件模块本身会声明uses(比如插件需要使用其他服务),可以从加载到ModuleLayer的插件模块中获取一个类,通过反射以该类作为调用者发起ServiceLoader调用。

示例代码:

public <S> ServiceLoader<S> loadServices(ModuleLayer layer, Class<S> serviceType) throws Exception {
    // 找到第一个提供该服务的插件模块
    Optional<Module> targetModule = layer.modules().stream()
            .filter(module -> module.findServiceProviders(serviceType).findAny().isPresent())
            .findFirst();
    
    if (targetModule.isEmpty()) {
        return ServiceLoader.load(layer, serviceType);
    }
    
    // 获取模块中的任意类作为调用者
    Class<?> callerClass = targetModule.get().getClasses()[0];
    Method loadMethod = ServiceLoader.class.getMethod("load", ModuleLayer.class, Class.class);
    // 以callerClass的类加载器上下文执行调用,确保调用者模块是插件模块
    return (ServiceLoader<S>) loadMethod.invoke(null, layer, serviceType);
}

优点:无需额外模块;缺点:依赖插件模块自身声明uses,如果插件仅提供服务不使用服务则会失效,且反射调用略繁琐。

内容的提问来源于stack exchange,提问作者Wexalian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:22:20