SPI全称是Service Provider Interface 服务提供接口,从名称上来看其实就是一个接口,我们看看官方是如何定义的。
Java的SPI(Service Provider Interface)机制是一种服务发现机制,允许在程序外部动态指定具体实现,从而实现模块间的解耦和扩展性。SPI机制的核心思想是将装配的控制权移到程序之外,类似于Spring的IOC机制。
说白了,其实就是比如你定义了一个接口,然后你可以供不同的开发者去实现。
话不多说,我们从一个例子来深入理解这个概念。
在jdk包下,也就是下面这个目录
有一个jar包叫做rt.jar,在这个包下,我们可以自己解压进去看看,我是在IDEA中打开的,
我们找到Driver接口,全类名是com.sql.Driver
这是这个接口的具体方法,我们都知道数据库必须有Driver驱动,但是市面上的数据库种类繁多,比如有Mysql,Oracle,Druid等等,所以不同的数据库就需要利用JDK包下的这个Driver接口去实现自己各自的Driver实现类。其实这就是SPI,那么我们如果项目中有很多依赖,都实现了这个Driver接口,我们还可以在运行时,通过某种加载器(ServiceLoader,待会后面会讲)实现灵活的切换,而不用去显示的通过注解去注入不同的实现类。
我们看看Mysql是如何去实现这个Driver接口的。
现在我们导入Mysql依赖。
找到Driver接口所在的位置包名:com.mysql.cj.jdbc;
图5
我们找到META-INF目录
注意这个配置文件,它的名称和jdk包下的Driver接口的全限定类名是一样的,然后我们点进去看看这个配置文件里面是什么东西。
发现它是我们前面提到的Driver实现类,也就是图5
所以我们发现一个步骤,就是如果我们要动态扩展某一个SPI接口,必须在META-INF目录下面创建一个services包,然后在包下创建一个文件,这个文件的名字就是与你要扩展的那个接口的全限定类名是一样的文件。然后文件里面填写你的具体实现类的全限定类名。
然后这就代表我们扩展了SPI了。
现在我们通过ServiceLoader加载器,看看我们程序总共有多少个实现了SPI的Driver接口的。
我们输入以下代码:
ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = drivers.iterator();
while (iterator.hasNext()) {
Driver driverService = iterator.next();
System.out.println("项目中对driver的SPI接口实现类有:" + driverService);
}
输出打印结果:
我们也可以这样看:
点这个小箭头
总共是有3个的,那这里我们要用到的是Mysql的,我们怎么切换用哪种呢?
就是你的数据库配置源的Driver是如何配置的,那么就用的是哪一个SPI Driver实现类。
我需要用到的是Mysql的,所以是这么配置的:
以后你想用其他的Driver,那么你就可以换上你自己的Driver实现类的全限定类名就可以实现切换。
这就是Driver的SPI,提供给不同的数据库厂商实现各自不同数据库的Driver逻辑。
大家也可以自己去写一个例子,这边我看到一个不错的demo例子,你们可以去找到以下这篇文章:
写的也十分不错
Java SPI概念、实现原理、优缺点、应用场景、使用步骤、实战SPI案例-CSDN博客
最后总结一下SPI的具体操作步骤:
- 找到你要扩展的SPI接口,拿到权限定类名
- 创建一个META-INF 目录,在目录下创建services包,创建以SPI接口的全限定类名的文件
- 文件内容写你的SPI 实现类的全限定类名。
- 使用ServiceLoader加载器去实现动态切换。