Java 中经常被提到的 SPI 到底是什么?
作者:互联网
Java
程序员在日常工作中经常会听到 SPI
,而且很多框架都使用了 SPI
的技术,那么问题来了,到底什么是 SPI
呢?今天阿粉就带大家好好了解一下 SPI。
SPI 概念
SPI
全称是 Service Provider Interface
,是一种 JDK
内置的动态加载实现扩展点的机制,通过 SPI
技术我们可以动态获取接口的实现类,不用自己来创建。
这里提到了接口和实现类,那么 SPI
技术上具体有哪些技术细节呢?
- 接口:需要有一个功能接口;
- 实现类:接口只是规范,具体的执行需要有实现类才行,所以不可缺少的需要有实现类;
- 配置文件:要实现
SPI
机制,必须有一个与接口同名的文件存放于类路径下面的META-INF/services
文件夹中,并且文件中的每一行的内容都是一个实现类的全路径; - 类加载器
ServiceLoader
:JDK
内置的一个类加载器,用于加载配置文件中的实现类;
举个栗子
上面说了 SPI
的几个概念,接下来阿粉就通过一个栗子来带大家感受一下具体的用法。
第一步
创建一个接口,这里我们创建一个解压缩的接口,其中定义了压缩和解压的两个方法。
package com.example.demo.spi;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-10-08 21:31<br>
* <b>Desc:</b>无<br>
*/
public interface Compresser {
byte[] compress(byte[] bytes);
byte[] decompress(byte[] bytes);
}
第二步
再写两个对应的实现类,分别是 GzipCompresser.java
和 WinRarCompresser.java
代码如下
package com.example.demo.spi.impl;
import com.example.demo.spi.Compresser;
import java.nio.charset.StandardCharsets;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-10-08 21:33<br>
* <b>Desc:</b>无<br>
*/
public class GzipCompresser implements Compresser {
@Override
public byte[] compress(byte[] bytes) {
return"compress by Gzip".getBytes(StandardCharsets.UTF_8);
}
@Override
public byte[] decompress(byte[] bytes) {
return "decompress by Gzip".getBytes(StandardCharsets.UTF_8);
}
}
package com.example.demo.spi.impl;
import com.example.demo.spi.Compresser;
import java.nio.charset.StandardCharsets;
/**
* <br>
* <b>Function:</b><br>
* <b>Author:</b>@author ziyou<br>
* <b>Date:</b>2022-10-08 21:33<br>
* <b>Desc:</b>无<br>
*/
public class WinRarCompresser implements Compresser {
@Override
public byte[] compress(byte[] bytes) {
return "compress by WinRar".getBytes(StandardCharsets.UTF_8);
}
@Override
public byte[] decompress(byte[] bytes) {
return "decompress by WinRar".getBytes(StandardCharsets.UTF_8);
}
}
第三步
创建配置文件,我们接着在 resources
目录下创建一个名为 META-INF/services
的文件夹,在其中创建一个名为 com.example.demo.spi.Compresser
的文件,其中的内容如下:
com.example.demo.spi.impl.WinRarCompresser
com.example.demo.spi.impl.GzipCompresser
注意该文件的名称必须是接口的全路径,文件里面的内容每一行都是一个实现类的全路径,多个实现类就写在多行里面,效果如下。
第四步
有了上面的接口,实现类和配置文件,接下来我们就可以使用 ServiceLoader
动态加载实现类,来实现 SPI
技术了,如下所示:
package com.example.demo;
import com.example.demo.spi.Compresser;
import java.nio.charset.StandardCharsets;
import java.util.ServiceLoader;
public class TestSPI {
public static void main(String[] args) {
ServiceLoader<Compresser> compressers = ServiceLoader.load(Compresser.class);
for (Compresser compresser : compressers) {
System.out.println(compresser.getClass());
}
}
}