Spring Boot应用CLI启动无法加载SPI实现的问题排查及解决
SPI接口在IDEA启动可正常加载、CLI启动加载失败问题
场景说明
我在Maven模块A中定义了SPI接口,在Spring Boot应用所在的模块B中,配置了META-INF/services/<interface-named-file>文件。
模块A核心代码
public class NotificationResultPluginProvider { private static NotificationResultPluginProvider notificationResultPluginProvider; private NotificationResultPlugin notificationResultPlugin; private NotificationResultPluginProvider() { final ServiceLoader<NotificationResultPlugin> loader = ServiceLoader.load(NotificationResultPlugin.class); final Iterator<NotificationResultPlugin> it = loader.iterator(); if (it.hasNext()) { notificationResultPlugin = it.next(); } } public static synchronized NotificationResultPluginProvider getInstance() { if (null == notificationResultPluginProvider) { notificationResultPluginProvider = new NotificationResultPluginProvider(); } return notificationResultPluginProvider; } public NotificationResultPlugin getNotificationResultPlugin() { return notificationResultPlugin; } }
我在Spring Boot应用模块B中自行实现了NotificationResultPlugin接口。
问题表现
- 通过IntelliJ启动Spring Boot应用时,以下代码中的
it.hasNext()返回true,可以正常获取到notificationResultPlugin实例,应用运行符合预期:
if (it.hasNext()) { notificationResultPlugin = it.next(); }
- 通过CLI启动应用时(先解压jar包再启动,启动命令如下),
it.hasNext()返回false,无法找到模块B中对应的SPI实现类,应用无法正常运行:
jar -xf <jar file>; java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=12341 -cp 'BOOT-INF/lib/*:BOOT-INF/classes' <main/class/fqn>;
补充信息
该Spring Boot应用不是Web应用,没有对外暴露REST接口,仅引入了spring-kafka的最小依赖。
解决方案
最终排查发现,ServiceLoader无法加载SPI实现的核心原因是应用启动时META-INF/services目录不在类路径下,调整类路径配置即可解决,修改后的启动参数如下:
-cp 'BOOT-INF/lib/*:BOOT-INF/classes:.'
参数末尾额外添加的.代表将当前目录加入类路径,解压后的jar包根目录下的META-INF文件夹就可以被正常读取了。
内容的提问来源于stack exchange,提问作者Tyagi Akhilesh
相关产品推荐
相关产品推荐

