1 前言
SPI,即Service Provider Interface。面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
Java SPI 就是提供这样的一个机制:为某接口(interface)寻找服务实现(impl)的机制。Java SPI 的具体约定为:当服务接口的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里(可置于resources目录下)创建一个以服务接口命名的文件,该文件内容写入实现该服务接口的具体实现类(全限定类名,多个实现类换行写入即可)而当外部程序装配此模块时,就能通过该jar包下META-INF/services/里的接口服务配置文件找到具体实现类,并装载实例化,完成模块的注入。基于这样的约定就能很好的找到服务接口的实现类,而不需要在代码里制定。
jdk提供服务实现查找的一个工具类,即:java.util.ServiceLoader。
2 使用
2.1 创建服务接口及其实现类
创建服务接口:
package com.xiaoxu.spi;
public interface XiaoxuSpiTest {
void spiRun();
}
创建该服务接口的两个实现类:
package com.xiaoxu.spiImpl;
import com.xiaoxu.spi.XiaoxuSpiTest;
/**
* @author xiaoxu
* @date 2022-08-30 7:45
* FruitMall:com.xiaoxu.spiImpl.XiaoxuSpiTestImpl
*/
public class XiaoxuSpiTestImpl implements XiaoxuSpiTest {
@Override
public void spiRun() {
System.out.println("哈哈哈,我是实现的第一个spi哦");
}
}
package com.xiaoxu.spiImpl;
import com.xiaoxu.spi.XiaoxuSpiTest;
/**
* @author xiaoxu
* @date 2022-09-01 7:40
* FruitMall:com.xiaoxu.spiImpl.XiaoxuSpiUseImpl
*/
public class XiaoxuSpiUseImpl implements XiaoxuSpiTest {
@Override
public void spiRun() {
System.out.println("我是spi使用的第二个实现~");
}
}
路径如下:
2.2 约定:在resources目录下新建服务接口发现的配置文件
路径为:src\main\resources\META-INF\services\com.xiaoxu.spi.XiaoxuSpiTest
配置文件为txt文件,文件名为接口的全限定类名:
文件内容如下,多个实现类,在txt中换行写入即可:
com.xiaoxu.spiImpl.XiaoxuSpiTestImpl
com.xiaoxu.spiImpl.XiaoxuSpiUseImpl
2.3 使用服务发现的工具类:ServiceLoader
package com.xiaoxu.demo;
import com.xiaoxu.spi.XiaoxuSpiTest;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ServiceLoader;
/**
* @author xiaoxu
* @date 2022-08-30 7:47
* FruitMall:com.xiaoxu.demo.TestSpi
*/
public class TestSpi {
@SuppressWarnings(value = "deprecation")
public static void main(String[] args) {
ServiceLoader<XiaoxuSpiTest> serviceLoader = ServiceLoader.load(XiaoxuSpiTest.class);
CollectionUtils.forAllDo(serviceLoader,(obj)->{
System.out.println("开始了");
obj.spiRun();
});
}
}
执行结果如下: