什么是SPI?
SPI 全称为 (Service Provider Interface) 服务提供接口,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。
Java SPI 应用实例
当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务实现的工具类是:java.util.ServiceLoader。
Java JDBC中定义了数据库驱动接口java.sql.Driver
,并没有具体的实现,具体的实现都是由不同厂商来提供的。在使用时具体调用哪个厂商的实现呢,一种方式是代码指定,还有一种就是用SPI用配置文件的方式来指定。下面的例子就是模拟第二种情况。
1. SPI接口
package com.pjn.java.spi;
public interface TestDriver {
void connect(String url);
}
模拟一个数据库连接的驱动接口
2. SPI具体实现
package com.pjn.java.spi;
public class MySQLDriver implements TestDriver {
@Override
public void connect(String url) {
System.out.println("mysql connect " + url);
}
}
模拟MySQLDriver是连接MySQL数据库的驱动
package com.pjn.java.spi;
public class OracleDriver implements TestDriver {
@Override
public void connect(String url) {
System.out.println("oracle connect " + url);
}
}
模拟OracleDriver是连接Oracle数据库的驱动
3. 增加META-INF目录文件
Resource下面创建META-INF/services 目录里创建一个以服务接口命名的文件com.pjn.java.spi.TestDriver
内容为接口的具体实现
com.pjn.java.spi.MySQLDriver
4. 使用 ServiceLoader 来加载配置文件中指定的实现
public class Test {
public static void main(String[] args) {
TestDriver targetDriver = null;
ServiceLoader<TestDriver> drivers = ServiceLoader.load(TestDriver.class);
for (TestDriver driver : drivers) {
targetDriver = driver;
break;//只取配置第一个实现
}
if (targetDriver == null) {
throw new RuntimeException("接口实现未配置");
}
targetDriver.connect("localhost:3306/testdb?user=root&password=123456");
}
}
输出结果
mysql connect localhost:3306/testdb?user=root&password=123456
SPI的用途
数据库DriverManager、Spring、ConfigurableBeanFactory、Dubbo SPI等都用到了SPI机制。
SPI缺点
通过上面的解析,可以发现,我们使用SPI查找具体的实现的时候,需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要实现。这应该也是最大的缺点,需要把所有的实现都实例化了,即便我们不需要,也都给实例化了。
总结
将接口实现的选择由硬编码指定改为通过配置文件来指定,更加灵活。SPI机制在实际开发中使用得场景也有很多,特别是统一标准的不同厂商实现,当有关组织或者公司定义标准之后,具体厂商或者框架开发者实现,之后提供给开发者使用。因此要是你开发了一个框架,某些功能都有默认实现,然后通过SPI的方式让用户可以自定义实现功能,使框架变得更灵活。Dubbo就是通过Dubbo SPI(类似Java SPI)来让用户可以指定使用自己使用集群调用策略来而不是被限制在框架中定义好的那几种策略。
关于Dubbo SPI的扩展阅读:https://blog.csdn.net/pange1991/article/details/86760193