# java SPI机制 [参考bilibili的视频](https://www.bilibili.com/video/BV1RY4y1v7mN/) 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中定义网络的连接接口 ```java 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依赖 ```groovy 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 ```java 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"配置文件 ```tex space.anyi.MoblieInternetService ``` 2. 模块simple-isp-unicom - 模块simple-isp-unicom引入simple-api依赖 ```groovy 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 ```java 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"配置文件 ```tex space.anyi.UnicomInternetService ``` ### Service Loader加载Service - 模块simple-compnay引入依赖 ```groovy 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并使用 ```java 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 services = ServiceLoader.load(InternetService.class); //遍历服务 for (InternetService service : services) { //使用服务 service.connect(); } } } ``` 运行项目实现开箱即用的效果 ![](http://tuchuang.anyi.space/imgs/Snipaste_2025-10-13_13-59-04.png) ## SPI总结 - 作用:提供了一种组件发现和注册的方式,可以用于实现各种插件,或者灵活替换框架使用的组件 - 优点:基于面向接口编程,优雅地实现模块之间的解藕 - 设计思想:面向接口+配置文件+反射技术 - 应用场景:JDBC,SLF4J,Servlet容器初始化等等