|
|
@@ -0,0 +1,266 @@
|
|
|
+# 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<InternetService> services = ServiceLoader.load(InternetService.class);
|
|
|
+ //遍历服务
|
|
|
+ for (InternetService service : services) {
|
|
|
+ //使用服务
|
|
|
+ service.connect();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ```
|
|
|
+
|
|
|
+ 运行项目实现开箱即用的效果
|
|
|
+
|
|
|
+ 
|
|
|
+
|
|
|
+## SPI总结
|
|
|
+
|
|
|
+- 作用:提供了一种组件发现和注册的方式,可以用于实现各种插件,或者灵活替换框架使用的组件
|
|
|
+- 优点:基于面向接口编程,优雅地实现模块之间的解藕
|
|
|
+- 设计思想:面向接口+配置文件+反射技术
|
|
|
+- 应用场景:JDBC,SLF4J,Servlet容器初始化等等
|
|
|
+
|