java SPI机制
java SPI机制
Service Provider Interface
java1.6引入的,基于ClassLoader的发现并加载服务的机制
核心概念
- Service:提供服务的接口,服务的标准
- Service Provider:服务的具体实现,服务的提供者
- Service Loader:服务的加载器,通过Server Loader加载具体的服务实现
SPI的工作机制
JDBC技术典型的SPI实践案例,JDK通过Service接口,具体的数据库厂商负责具体的Service Provider实现
- 应用程序启动时通过Service Loader加载第三方jar中的Service Provider(Service的具体实现类)
- 在应用程序中通过Service的形式使用,不用关心具体的实现细节
SPI规范
-
配置文件规范
-
文件路径:必须在jar包中META-INF/services目录下
-
文件名称:Service接口的全限定名
-
文件内容:Service Provider实现类的全限定名,每个实现类单独占一行
e.g
在MySQL的驱动jar中的META-INF/services目录中有一个"java.sql.Driver"文件,文件内容为"com.mysql.cj.jdbc.Driver"
-
-
Service Provide类必须具备无参的构造方法
- 因为使用反射技术创建实现类时需要使用无参构造器
-
保证能加载到配置文件和Service
- 方式一:将Service Provider的jar包放到classpath中
- 方式二:将jar放到jre的扩展目录中
- 方式三:自定义一个ClassLoader
SPI实现案例
一个公司A,需要连接互联网.公司A定义连接网络的API(Service),由中国移动(Service Provider)和中国联通(Serivce Provider)负责提供具体的网络连接服务
SPI实现流程
- 在service定义接口
- 在service provider中实现service,并在META-INF/services目录下提供对应的配置文件
- 在应用程序中使用ServiceLoader加载service,然后进行使用
定义service接口
- 定义service接口
- 在模块Simple-api中定义网络的连接接口
package space.anyi;
/**
* @ProjectName: SPI-learn
* @FileName: InternetService
* @Author: 杨逸
* @Data:2025/10/13 11:13
* @Description: 网络服务接口
*/
public interface InternetService {
void connect();
}
Service Provider实现Service接口
- 模块simple-isp-moblie
- 模块simple-isp-moblie引入simple-api依赖
plugins {
id 'java'
}
group 'space.anyi'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
//引入simple-api依赖,以项目的形式
implementation(project(":simple-api"))
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
test {
useJUnitPlatform()
}
- 模块simple-isp-moblie实现service
package space.anyi;
/**
* @ProjectName: SPI-learn
* @FileName: MoblieInternetService
* @Author: 杨逸
* @Data:2025/10/13 11:24
* @Description: 中国移动提供的网络服务实现
*/
public class MoblieInternetService implements InternetService{
@Override
public void connect() {
System.out.println("使用中国移动提供的网络服务");
}
}
- 在"META-INF/services"目录下提供"space.anyi.InternetService"配置文件
space.anyi.MoblieInternetService
- 模块simple-isp-unicom
-
模块simple-isp-unicom引入simple-api依赖
plugins { id 'java' } group 'space.anyi' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { //引入simple-api依赖,以项目的形式 implementation(project(":simple-api")) testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { useJUnitPlatform() } -
模块simple-isp-unicom实现service
package space.anyi;
/**
* @ProjectName: SPI-learn
* @FileName: UnicomInternetService
* @Author: 杨逸
* @Data:2025/10/13 11:37
* @Description: 中国联通提供的网络服务实现
*/
public class UnicomInternetService implements InternetService{
@Override
public void connect() {
System.out.println("使用中国联通提供的网络服务");
}
}
在"META-INF/services"目录下提供"space.anyi.InternetService"配置文件
space.anyi.UnicomInternetService
Service Loader加载Service
-
模块simple-compnay引入依赖
plugins { id 'java' } group 'space.anyi' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { //可以全都使用,也可以按需使用 //服务提供,使用中国移动的网络服务 implementation(project(":simple-isp-moblie")) //服务提供,使用中国联通的网络服务 implementation(project(":simple-isp-unicom")) //接口依赖 implementation(project(":simple-api")) testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { useJUnitPlatform() } -
使用加载Service并使用
package space.anyi; import java.util.ServiceLoader; /** * @ProjectName: SPI-learn * @FileName: CompanyApplication * @Author: 杨逸 * @Data:2025/10/13 11:45 * @Description: 公司A连接互联网的应用 */ public class CompanyApplication { public static void main(String[] args) { //通过java.util.ServiceLoader加载服务 ServiceLoader<InternetService> services = ServiceLoader.load(InternetService.class); //遍历服务 for (InternetService service : services) { //使用服务 service.connect(); } } }运行项目实现开箱即用的效果

SPI总结
- 作用:提供了一种组件发现和注册的方式,可以用于实现各种插件,或者灵活替换框架使用的组件
- 优点:基于面向接口编程,优雅地实现模块之间的解藕
- 设计思想:面向接口+配置文件+反射技术
- 应用场景:JDBC,SLF4J,Servlet容器初始化等等
Comments