java SPI机制

参考bilibili的视频

Service Provider Interface

java1.6引入的,基于ClassLoader的发现并加载服务的机制

核心概念

  • Service:提供服务的接口,服务的标准
  • Service Provider:服务的具体实现,服务的提供者
  • Service Loader:服务的加载器,通过Server Loader加载具体的服务实现

SPI的工作机制

JDBC技术典型的SPI实践案例,JDK通过Service接口,具体的数据库厂商负责具体的Service Provider实现

  1. 应用程序启动时通过Service Loader加载第三方jar中的Service Provider(Service的具体实现类)
  2. 在应用程序中通过Service的形式使用,不用关心具体的实现细节

SPI规范

  1. 配置文件规范

    • 文件路径:必须在jar包中META-INF/services目录下

    • 文件名称:Service接口的全限定名

    • 文件内容:Service Provider实现类的全限定名,每个实现类单独占一行

    e.g

    在MySQL的驱动jar中的META-INF/services目录中有一个"java.sql.Driver"文件,文件内容为"com.mysql.cj.jdbc.Driver"

  2. Service Provide类必须具备无参的构造方法

    • 因为使用反射技术创建实现类时需要使用无参构造器
  3. 保证能加载到配置文件和Service

    • 方式一:将Service Provider的jar包放到classpath中
    • 方式二:将jar放到jre的扩展目录中
    • 方式三:自定义一个ClassLoader

SPI实现案例

一个公司A,需要连接互联网.公司A定义连接网络的API(Service),由中国移动(Service Provider)和中国联通(Serivce Provider)负责提供具体的网络连接服务

SPI实现流程

  1. 在service定义接口
  2. 在service provider中实现service,并在META-INF/services目录下提供对应的配置文件
  3. 在应用程序中使用ServiceLoader加载service,然后进行使用

定义service接口

  1. 定义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接口

  1. 模块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
  1. 模块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容器初始化等等