Java的SPI服务发现机制详解
在学习SpringBoot时经常会听到SPI这个术语,那什么是SPI呢?SPI有什么作用呢?与SpringBoot有什么关联呢?下面我们一起来探究下。
什么是SPI
SPI全拼是Service Provider Interface,是Java提供的一种服务发现机制。它会加载所有jar包中classpath路径下的META-INF/services文件夹下的文件,自动加载文件里所定义的类。通过这种机制为许多框架提供了扩展的可能,比如JDBC中就使用了SPI机制。
下面是JDK定义了JDBC接口,不同的数据库厂商实现了JDBC的接口,不同的数据库在自己的实现jar包中META-INF/services文件夹下创建接口名称文件(接口名是需要实现的接口全类名),文件内容就是实现接口的具体类。
相当于java定义了规范,具体的由第三方实现,就是java和第三方提供商的一个协定。
下面先来看个案例,弄懂SPI的用法。
SPI接口定义
第一步定义SPI接口,相当于定义一个规范,实现交给第三方。
创建一个maven项目,不要创建成springboot项目,因为默认的springboot项目打的jar和普通的maven项目jar包的目录结构不一致,导致第三方引用接口失败。
把项目打一个Jar包。这就是一个接口定义包,很简单。
SPI接口实现
我们创建两个接口实现的项目,创建两个项目是为了说明接口可以有不同的第三方实现。
这里也是创建普通的maven项目,不要创建成功springboot项目,原因与上面一样。
创建第一个项目
创建一个mySqlDatabase项目。
实现Connection接口。
在classpath路径下创建META-INF/services目录,在目录中创建接口全类路径con.study.database.Connection的文件,文件中的内容就是实现Connection接口的实现类名称。
打成jar包,供应用程序调用。
创建第二个项目
创建项目MyOracleDatabase。
实现Connection接口。
在classpath路径下创建META-INF/services目录,在目录中创建接口全类路径con.study.database.Connection的文件,文件中的内容就是实现Connection接口的实现类名称com.study.MyOracleConnection。
打成jar包供应用程序调用。
应用程序使用SPI接口
应用程序首先导入上面两个jar包,真正使用的时候导入一个实现了Connection接口的jar包就可以了,这里是为了演示所以导入的两个实现的jar包。
<dependency>
<groupId>org.example</groupId>
<artifactId>mySqlDatabase</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>MyOracleDatabase</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
调用JDK的ServiceLoader.load接口加载lib库下面的所有jar包的classpath路径下META-INF/services目录下的com.study.database.Connection文件。
package com.example.importstudy.service;
import com.study.database.Connection;
import java.util.Iterator;
import java.util.ServiceLoader;
public class TestMyDataBaseConnect {
public static void main(String[] args) {
ServiceLoader<Connection> load = ServiceLoader.load(Connection.class);
Iterator<Connection> iterator = load.iterator();
while (iterator.hasNext()) {
Connection connection = iterator.next();
connection.connect();
}
}
}
测试结果
Connected to the target VM, address: '127.0.0.1:61535', transport: 'socket'
mysql connection
oracle connection
Disconnected from the target VM, address: '127.0.0.1:61535', transport: 'socket'
可以看到两个实现类都被加载了,但是我们在测试代码中并没使用任何实现类,只是使用了定义的Connection接口。这就是SPI的原理,加载META-INF/services下的接口名称文件,把文件中的实现类加载到JVM中。
总结
SPI是Java提供的一种服务发现机制,让框架只需要定义好接口,实现交给第三方,第三方只要在META-INF/service目录下添加接口全类名文件,文件中填写实现类的全类名即可,框架启动时会自动加载所有jar包中META-INF/service目录下以接口命名的文件。这样应用程序仅需要简单的导入实现类的Jar包就可以使用接口的功能了,用户可以很方便的替换不同的实现,而不需要改动业务调用代码。
springBoot自动装配就是借鉴了Java的SPI服务发现机制,springBoot在启动是会自动扫描所有jar包下的META-INF/spring.factories文件并加载其中的org.springframework.boot.autoconfigure.EnableAutoConfiguration关联的类。
请先 后发表评论~