一:什么是SPI
SPI:“服务提供者的接口”,是一种服务发现机制
用于实现框架或库的扩展点,允许在运行时动态地插入或更换组件实现。
它提供了一个框架(JDK1.6后ServiceLoader)来发现和加载服务实现,使得软件模块能够灵活地选择和使用不同的服务提供商。
在java中通俗讲就是:
- 对框架或第三方jar包提供者来说可制定规范,提供给开发者可扩展性
- 对开发者来说可以根据需要轻松替换框架或第三方jar包中提供了SPI机制的接口的实现
java_SPI_15">二:java SPI示例
1.SPI服务提供方
SPI服务提供方架构图:
定义接口规范:
java">public interface SpiService {/*** 呼叫方式*/void call();
}
加载具体的服务实现:
java">package com.lmy.config;import com.lmy.service.SpiService;import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;/*** @author : lmy* @date : 2024/9/14 上午 11:35* 加载具体的服务实现*/
public class SpiServiceLoader {private static volatile SpiServiceLoader LOADER;private final SpiService spiService;private final List<SpiService> spiServiceList;/*** 加载服务* */private SpiServiceLoader() {ServiceLoader<SpiService> loader = ServiceLoader.load(SpiService.class);List<SpiService> list = new ArrayList<>();for (SpiService spiService : loader) {list.add(spiService);}spiServiceList = list;if (!list.isEmpty()) {// 取第一个spiService = list.get(0);} else {spiService = null;}}/*** SpiServiceLoader 单例加载* */public static SpiServiceLoader getLOADER() {if (LOADER == null) {synchronized (SpiServiceLoader.class) {if (LOADER == null) {LOADER = new SpiServiceLoader();}}}return LOADER;}public void call(){if(spiServiceList.isEmpty()){System.out.println("SpiService服务未加载!");}else {SpiService spiService = spiServiceList.get(0);spiService.call();}}}
默认实现:
java">package com.lmy.service.impl;import com.lmy.service.SpiService;/*** @author : lmy* @date : 2024/9/14 上午 10:58* 默认实现*/
public class SpiServiceImpl implements SpiService {@Overridepublic void call() {System.out.println("默认手机呼叫");}
}
指定服务实现方式:
须在resource下创建META-INF.services,文件名为接口全限定类名,配置为需要被加载的接口实现类的全限定类名
com.lmy.service.impl.SpiServiceImpl
项目打包发布本地:
2.SPI服务应用方开发者
开发者引入jar包使用服务:
java"><dependency><groupId>com.lmy</groupId><artifactId>SPI-interface</artifactId><version>1.0-SNAPSHOT</version>
</dependency>
java">package com.lmy.Spi;import com.lmy.config.SpiServiceLoader;
import org.junit.Test;/*** @author : lmy* @date : 2024/9/14 上午 11:48*/
public class SpiTest {@Testpublic void spiTest () {SpiServiceLoader loader = SpiServiceLoader.getLOADER();loader.call();}
}
执行结果:
开发者根据需要扩展替换为自己的服务实现:
java">package com.lmy.Spi.service;import com.lmy.service.SpiService;/*** @author : lmy* @date : 2024/9/14 下午 2:09*/
public class SpiServiceNewImpl implements SpiService {@Overridepublic void call() {System.out.println("卫星直呼");}
}
com.lmy.Spi.service.SpiServiceNewImpl
执行结果:
三:JavaSPI 机制的核心-ServiceLoader
上面代码可见是通过ServiceLoader 去加载具体的服务实现的
ServiceLoader 是从JDK1.6 开始提供的一个类,用于加载服务提供者。
进入源码可见:
其中 String PREFIX = “META-INF/services/”;
这个就是JDK的SPI功能规定的具体服务实现的配置信息文件所在的目录 META-INF/services/
JDK的SPI规定 服务实现者需要在 META-INF/services/ 目录下 新建文件名为 SPI接口全限定类名的文件
文件内容为 服务实现者需要被加载的具体类的全限定类名
具体源码底层实现参考:https://blog.csdn.net/qq_37883866/article/details/139000021 中第3点