SPI机制说明
什么是SPI
Service Provider Interface 机制是Java提供的一套用来被第三方实现或扩展的API,他可以用来启用框架扩展和替换组件。通过“基于接口的编程 + 策略模式 + 配置文件”组合实现的动态加载机制。SPI机制为某个接口寻找服务实现的机制,就是将装配的控制权转移到程序之外,其核心思想是解耦。
机制图
使用示例
1. 定义服务接口
接口定义了服务提供者必须实现的方法。
public interface MyService {
void doSomething();
}
2. 实现服务接口
服务提供者需要实现这个接口,提供服务操作。
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
3. 注册服务提供者
为了让组件能够找到服务提供者,需要在 /META-INF/services 目录下创建一个文件,文件的名称是服务接口的完全限定名(例如上面的com.example.MyService)。在这个文件中列出所有实现该接口的类的完全限定名,每行一个。
如,文件 META-INF/services/com.example.MyService
的内容:
com.example.MyServiceImpl
4. 加载和使用服务
当其它服务需要这个服务时,可以使用 java.util.ServiceLoader 类来加载和实例化服务的实现。ServiceLoader会查找classpath下META-INF/services目录中的配置文件,并根据配置文件中的类名加载和实例化服务提供者。
ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class);
for (MyService service : serviceLoader) {
service.doSomething();
}
加载所有注册为MyService接口实现的服务提供者,然后可以遍历这些服务提供者并使用它们。
优点
- 解耦:服务接口和实现是分离的,这使得它们可以独立开发和部署,降低了模块之间的耦合度。
- 动态加载:服务提供者可以在运行时被动态加载,这使得应用程序可以在不重启的情况下更新或扩展其功能。
- 可扩展性:通过添加新的服务提供者,可以轻松地为应用程序添加新功能。
总结
允许开发人员创建可扩展和可拔插的应用程序。通过使用服务接口、实现类和服务加载器,开发人员可以轻松地将不同的组件组合在一起,从而构建出功能强大且易于维护的应用程序。
SPI机制的实际应用
JDBC DriverManager
数据库连接操作就是使用Java的SPI扩展机制来实现的。
定义JDBC接口
首先在Java中定义了接口 java.sql.Driver ,并没有具体的实现,具体的实现都是由不同的厂商提供的。
mysql实现
开源代码
com.mysql.cj.jdbc.Driver 就是mysql对于java的jdbc接口实现。
然后再看java是怎么通过SPI加载数据库驱动的
看 loadInitialDrivers
是不是和刚开始讲的很熟悉。
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
迭代器会遍历搜索classpath下以及jar包中所有的 META-INF/services
目录下的 java.sql.Driver
文件,然后找到具体的实现类。
你在加载器这里打个断点,运行项目就会看到类加载器加载数据库连接的驱动类。
java中有很多地方都用到了这个SPI机制,比如日志门面 JCL 的各种实现,springboot自动装配等等,有时间都可以研究一下。