serviceconfigurationerror 根本原因是 spi 配置文件未被 jvm 正确加载,涉及路径(必须为 meta-inf/services/接口全限定名)、格式(纯文本、无 bom、每行一个实现类)、类加载器隔离(需确保上下文类加载器可访问实现类)及模块声明(jdk 9+ 需 provides/uses)。

ServiceConfigurationError 本质是 SPI 配置文件没被 JVM 正确读到,不是代码写错了,而是资源路径、格式或类加载器出了问题。
service 文件必须放在 META-INF/services/ 下且命名严格匹配接口全限定名
Java SPI 要求配置文件路径和命名完全符合规范,否则 ServiceLoader.load() 根本不会去扫描它。
- 文件路径必须是
META-INF/services/com.example.MyServiceInterface(注意大小写、无扩展名) - 文件内容只能是实现类的全限定名,每行一个,末尾不能有空格或 BOM 字符
- 如果接口是
java.util.spi.ToolProvider,那文件名就得是java.util.spi.ToolProvider,少一个点都不行 - IDE 编译时容易把
META-INF放错位置——Maven 项目要确保它在src/main/resources/META-INF/services/,不是src/main/java下
ClassCastException 或 NoClassDefFoundError 往往是因为类加载器隔离
ServiceLoader 默认用当前线程上下文类加载器(Thread.currentThread().getContextClassLoader())加载实现类。Web 容器、OSGi、模块化 JDK 中,这个加载器可能看不到你的实现类。
- 检查
Thread.currentThread().getContextClassLoader()是否能loadClass("com.example.MyServiceImpl"),不能就说明类路径断裂 - Tomcat 中常见:SPI 文件在 WAR 的
WEB-INF/classes,但实现类在某个 JAR 的lib/下,而该 JAR 被父类加载器加载,子加载器无法反向访问 - JDK 9+ 模块系统下,如果服务接口在模块 A,实现类在模块 B,B 必须在
module-info.java中声明provides com.example.MyServiceInterface with com.example.MyServiceImpl;,且 A 要uses com.example.MyServiceInterface;
ServiceConfigurationError 的 stack trace 里关键线索藏在 cause 中
这个异常本身只是容器,真正的问题在 getCause()。不看 cause,永远修不对。
立即学习“Java免费学习笔记(深入)”;
- 常见 cause 是
NoClassDefFoundError:说明类存在但依赖缺失(比如实现类引用了另一个未打包的类) - 也可能是
ClassNotFoundException:类名拼错、包路径错、或类根本没进 classpath - 还有
ExceptionInInitializerError:实现类的 static 块抛了异常(比如读配置失败),导致类初始化中断 - 调试时别只打印
e.toString(),一定要加e.getCause()和e.getCause().printStackTrace()
最麻烦的情况是:文件路径对、类名对、类加载器也对,但用了 Spring Boot 的 spring.factories 或自定义加载逻辑覆盖了原生 SPI ——这时候 ServiceLoader 根本没机会运行,错误也不会抛 ServiceConfigurationError。得先确认你真正在走 Java SPI 这条路。










