java中SPI(服务提供者的接口)
java中SPI(服务提供者的接口)
- 一:什么是SPI
- 二:java SPI示例
- 1.SPI服务提供方
- 2.SPI服务应用方开发者
- 三:JavaSPI 机制的核心-ServiceLoader
一:什么是SPI
SPI:“服务提供者的接口”,是一种服务发现机制
用于实现框架或库的扩展点,允许在运行时动态地插入或更换组件实现。
它提供了一个框架(JDK1.6后ServiceLoader)来发现和加载服务实现,使得软件模块能够灵活地选择和使用不同的服务提供商。
在java中通俗讲就是:
- 对框架或第三方jar包提供者来说可制定规范,提供给开发者可扩展性
- 对开发者来说可以根据需要轻松替换框架或第三方jar包中提供了SPI机制的接口的实现
二:java SPI示例
1.SPI服务提供方
SPI服务提供方架构图:
定义接口规范:
public interface SpiService {
/**
* 呼叫方式
*/
void call();
}
加载具体的服务实现:
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();
}
}
}
默认实现:
package com.lmy.service.impl;
import com.lmy.service.SpiService;
/**
* @author : lmy
* @date : 2024/9/14 上午 10:58
* 默认实现
*/
public class SpiServiceImpl implements SpiService {
@Override
public void call() {
System.out.println("默认手机呼叫");
}
}
指定服务实现方式:
须在resource下创建META-INF.services,文件名为接口全限定类名,配置为需要被加载的接口实现类的全限定类名
com.lmy.service.impl.SpiServiceImpl
项目打包发布本地:
2.SPI服务应用方开发者
开发者引入jar包使用服务:
<dependency>
<groupId>com.lmy</groupId>
<artifactId>SPI-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
package com.lmy.Spi;
import com.lmy.config.SpiServiceLoader;
import org.junit.Test;
/**
* @author : lmy
* @date : 2024/9/14 上午 11:48
*/
public class SpiTest {
@Test
public void spiTest () {
SpiServiceLoader loader = SpiServiceLoader.getLOADER();
loader.call();
}
}
执行结果:
开发者根据需要扩展替换为自己的服务实现:
package com.lmy.Spi.service;
import com.lmy.service.SpiService;
/**
* @author : lmy
* @date : 2024/9/14 下午 2:09
*/
public class SpiServiceNewImpl implements SpiService {
@Override
public 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点