开心一刻
今天去幼儿园接小侄女,路上聊起了天
小侄女:小叔,今天我吃东西被老师发现了
我:老师说了什么
小侄女:她说拿出来,跟小朋友一起分享
我:那你拿出来了吗
小侄女一脸可怜的看向我,说道:没有,我没有那么多鼻屎
SPI
概念
SPI 全称 Service Provider Interface ,直译过来就是: 服务提供接口 ,是不是有点抽象?
简单点理解,Java SPI 就是提供这样的一个机制:为某个接口寻找服务实现的机制
还是抽象?我们往下看它的具体实现就好理解了
实现三板斧
1、接口与实现
Animal 接口
Dog 实现
Cat 实现
2、配置文件
配置文件有点讲究,需要按这套规则来
2.1 在 src/main/resources/ 下建立目录: /META-INF/services ,位置和名字都必须严格按这个来,一字都不能差
2.2 在 /META-INF/services 目录下创建一个以接口全限定类名为名的文件: com.qsl.service.Animal ,没有额外的后缀
2.3 将接口实现类的全限定类名写入到 2.2 创建的文件中,一个实现占一行
3、ServiceLoader 加载
通过 ServiceLoader 进行加载,代码很简单,如下所示
正常情况下会输出如下内容
示例工程结构如下
至此,对 SPI 的感觉是不是没那么抽象了
简单理解, Java SPI 是 基于接口的编程 + 策略模式 + 配置文件 实现的动态加载机制
使用场景
不太好概括,一千个人眼中有一千个哈姆雷特
但是我们可以通过一些案例来形成自己的概括
1、DriverManager
不知道大家还记得 JDBC 的写法吗
我们去跟下 DriverManager 的源码
我们再看下 MySQL 驱动的包结构
2、SLF4J
具体源码我就不带大家去跟了,有兴趣的可以去看看:从源码来理解slf4j的绑定,以及logback对配置文件的加载 中的问题1
3、Spring SPI
Spring 有自己的 SPI 实现机制,和 JDK SPI 略有不同
Spring 是在 src/main/resources/META-INF 目录下创建 spring.factories ,里面以键值对的方式存放多个实现,类似如下
4、Dubbo SPI
Dubbo 又有自己的一套实现,配置文件需要放到 META-INF/dubbo 目录下
具体细节可查看其官方文档:Dubbo SPI
问题重现
此刻,大家是不是觉得 JDK SPI 很简单?
但正是这么简单的东西,楼主都碰到了问题,如下图所示
当时人就懵了!!!
问题排查
一度怀疑是不是 JDK SPI 还有额外的配置
因为是工作中的项目出了这个问题,所以我自建了一个 demo 来验证 实现三板斧
结果 demo 的执行是没问题的,这也就说明 JDK SPI 的实现就只有那三板斧,那问题出在哪了?
本着快速解决问题的目的,我换了一种实现方式,采用 Spring SPI
结果依然是有问题,同样是读不到 spring.factories 中的配置
正在一筹莫展之际,直觉告诉我是不是 maven 构建出了问题,所以我对项目进行了 package ,然后去看了下打好的包的目录结构
META-INF 目录下的 com.qsl.service.Animal 文件了?
肯定是 pom.xml 配置不对
我是万万没想到 pom.xml 会进行如上的配置(后面问了老同事,没特别的原因,就是简单的认为只会有 xml 和 yml 配置文件)
此刻,相信大家都知道怎么改了吧(去掉<includes>标签,或者在<includes>中加上)
然而楼主没用采用上述两种方案的任一一个,也没有改 pom.xml ,就问你气不气?
总结
1、 JDK SPI 的使用,就那三板斧,如果出了问题,不用想,肯定不是 JDK SPI 的问题
2、关于 SPI 的使用场景,楼主仍然不做概括(太菜,概括不好),大家自行去概括
3、关于 pom.xml
楼主之前写过一篇:Maven pom.xml中的元素modules、parent、properties以及import
但就是没讲 <build> ,下次补上,你们记得提醒我哦!