什么是 SPI 机制?
SPI(Service Provider Interface),是 JDK 内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,Java的SPI机制可以为某个接口寻找服务实现。
Java中SPI机制的主要思想是将装配的控制权转移到程序外,降低耦合度。
当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/
目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/
中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader
。
SPI机制的简单示例
定义一个搜索接口,搜索关键词是一个参数,返回对应的搜索结果:
public interface Search {
String search(String keywords);
}
文件搜索的具体实现:
public class FileSearchImpl implements Search{
@Override
public String search(String keywords) {
return "我是文件搜索功能的具体实现";
}
}
数据库搜索的具体实现:
public class DataBaseSearchImpl implements Search{
@Override
public String search(String keywords) {
return "我是数据库搜索功能的实现";
}
}
接下来可以在resources下新建META-INF/services/目录,然后新建接口全限定名的文件:com.kaiven.spi.Search
,里面加上我们需要用到的实现类。
com.kaiven.spi.DataBaseSearchImpl
com.kaiven.spi.FileSearchImpl
测试:
public class Main {
public static void main(String[] args) {
ServiceLoader<Search> load = ServiceLoader.load(Search.class);
for (Search search : load) {
System.out.println(search.search(""));
}
}
}
(好玩吧,哈哈)
SPI机制的典型应用
数据库
JDBC提供了统一的数据库相关操作的接口:
public interface Driver {
Connection connect(String url, java.util.Properties info)
throws SQLException;
boolean acceptsURL(String url) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
throws SQLException;
int getMajorVersion();
int getMinorVersion();
public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
这就是所谓的数据库驱动接口。
让我们看一下mysql是怎么实现的:
是不是和我们上面写的例子一模一样呢?哈哈。
(其他的应用场景可以自行百度扩展一下,特别是Spring的,想一下没有SpringBoot之前,是不是一大堆的xml配置文件)
SPI机制深入理解
SPI和API的区别?
使用SPI机制的时候,我们会将具体的实现抽离出程序外,也就是相当于接口的定义与实现不在同一个包中,比如 JDBC和MySQL驱动的实现。对于API的话,平时的开发中,会将接口与实现类放在同一个包中。前者强调调用方,后者强调实现方。
SPI机制的实现原理
(这个感兴趣的可以自行去查看JDK中ServiceLoader<S>
方法的具体实现,面试的话,这里你能讲出一些自己的理解就行了)
SPI机制的缺陷
- 不能按需加载,需要遍历所有实现,并实例化,然后再循环中才能找到我们的实现。如果不想使用某些实现类,或者某些类实例化很耗时,它也被载入并实例化,这就造成了浪费。
- 获取某个实现类的方式不够灵活,只能通过
Iterator
形式获取,不能根据某个参数来获取对应的实现类。 - 多线程使用 ServiceLoader 类的实例是不安全的。
2024.10.28
writeBy kaiven