bigywater.tech
Java · 160

ServiceLoader:Java SPI 如何发现实现类

Java SPI 是怎么发现实现类的?这篇用 classpath 与模块两种方式各做一遍,把惰性实例化、缓存、出错时机和模块声明逐项验证(已有的 ServiceLoader 相关文章在模块化一类里)。

B、A、C
service 文件里的顺序就是迭代顺序
迭代时才报错
service 文件写错的那一项,在迭代到它时才抛 ServiceConfigurationError
没有输出
module-path 里没有 impl 模块时的结果

classpath 上的 SPI

实测 · 本机执行输出
  ServiceLoader.load(...) 返回后,还没有迭代:没有任何构造器运行(惰性)
  先只看 providers 的类型(sl.stream().map(Provider::type)):[B, A, C]——仍然没有实例化
  第一次迭代(for-each):
    [B 的构造器运行]
    得到 B
    [A 的构造器运行]
    得到 A
    [C 的构造器运行]
    得到 C
  第二次迭代(缓存):
    没有再运行构造器,顺序 = [B, A, C]
  reload() 之后再迭代:
    [B 的构造器运行]
    得到 B
    [A 的构造器运行]
    得到 A
    [C 的构造器运行]
    得到 C
    [B 的构造器运行]
  findFirst() 只实例化第一个:
    得到 B

配置出错

实测 · 本机执行输出
  [不存在的类 Nope]
    [B 的构造器运行]
    B
  迭代时抛 ServiceConfigurationError: Provider Nope not found
  [存在但没有实现 Greeter 的类 Plain]
    [B 的构造器运行]
    B
  迭代时抛 ServiceConfigurationError: Plain not a subtype
  [只有带参构造器的类 NoDefault]
    [B 的构造器运行]
    B
  迭代时抛 ServiceConfigurationError: NoDefault Unable to get public no-arg constructor

模块系统

实测 · 本机执行输出
  [module-path 里有 api、impl、app]
    Hello(来自模块 impl)
    Hi(来自模块 impl)
  [module-path 里只有 api、app(没有 impl)]
    (没有输出:找不到任何实现)
  java --describe-module impl:
    impl file://<jtmp>/j20m/mods/impl/
    requires api
    requires java.base mandated
    provides spi.Greeter with impl.Hello impl.Hi
    contains impl

分步演示

ServiceLoader
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:ServiceLoader.load 使用不同类加载器(线程上下文类加载器)时的查找、Provider 静态工厂方法(provider() 方法)、模块 uses 缺失时的错误、JDK 17 与 27 的差异、多个同名 provider 的去重
    京ICP备2026067502号-1京公网安备11010502063487号