SPI服务发现机制

作者: 西魏陶渊明 博客: https://blog.springlearn.cn/
!!! tip 西魏陶渊明 莫笑少年江湖梦,谁不少年梦江湖 !!!
一、什么是SPI
SPI ,全称为 Service Provider Interface,是一种服务发现机制。JDK中的SPI是通过在ClassPath路径下的META-INF/services文件夹查找扩展文件,自动加载文件里所定义的类。
在小编的理解来,觉得它更是一种思想。即找到服务的接口, 美其名曰: 服务发现机制思想。很多开源框架都有借用这种思想,比如dubbo、jdbc。
二、SPI在JDK中如何使用
SPI在JDK中,我们可以使用 ServiceLoader 类进行使用。

1. 前提准备
1public interface SpiService {
2 String say();
3}两个实现类
1public class ASpiServiceImpl implements SpiService {
2 static {
3 System.out.println("static init a");
4 }
5
6 {
7 System.out.println("init a");
8 }
9
10 @Override
11 public String say() {
12 return "A";
13 }
14}1public class BSpiServiceImpl implements SpiService {
2 static {
3 System.out.println("static init b");
4 }
5
6 {
7 System.out.println("init b");
8 }
9 @Override
10 public String say() {
11 return "B";
12 }
13}2. 进行配置
在resources中创建META-INF/services目录

1│ └── resources
2│ └── META-INF
3│ └── services
4│ └── com.github.easylog.spi.SpiServicecom.github.easylog.spi.SpiService文件内容
1com.github.easylog.spi.impl.ASpiServiceImpl
2com.github.easylog.spi.impl.BSpiServiceImpl3. 使用
通过ServiceLoader类我们可以加载到所有配置的实现类,并对实现类进行处理。需要注意一点的是,看4使用注意。

1public class SpiTester {
2 public static void main(String[] args) {
3 ServiceLoader<SpiService> spiServices = ServiceLoader.load(SpiService.class);
4 Iterator<SpiService> iterator = spiServices.iterator();
5 while (iterator.hasNext()) {
6 SpiService next = iterator.next();
7 System.out.println(next.say());
8 }
9 }
10}4. 使用注意
可以看下小编前面声明的两个实现类,都定义了静态代码块和非静态代码块。正常情况当这个字节码被加载,就会执行静态代码块里面的内容,但是实际运行时候却没有执行, 其实是有原因的。

可以看到第二个参数是false。即加载时候不进行初始化。
三、Dubbo中服务发现思想
服务发现这种思想的特点是: 代码不是硬编码的方式,而是可配置的。只要将要支持的实现类放到指定配置文件下面,就会自动被加载起来了。然后代码中只关心使用即可。我们可以利用这种思想来实现, 框架的扩展,比如前面说了。Dubbo会利用SPI的思想进行,加载用户自定义的过滤器。
这种思想特别适合做服务扩展。现在大多数开源框架中都会使用到这种思想。
1. 定义过滤器

1@Activate(group = { Constants.PROVIDER })
2public class ProviderHelloFilter implements Filter {
3
4 @Override
5 public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
6 System.out.pringln("hello ok!");
7 return invoker.invoke(invocation);
8 }
9
10}2. 添加配置文件
META-INF/dubbo/Interal/com.alibaba.dubbo.rpc.Filter
默认支持的过滤器

利用SPI原理,我们自定义一个过滤器

3. 使用
其实API跟JDK中使用ServiceLoader的方式,非常类同。唯一不同的是Dubbo中是使用ExtensionLoader。因为dubbo中做了一些特殊的增强处理。比如在配置文件中支持自定义一个别名key。如上图hello就是key。通过getExtension("hello")就能获取指定的实现类。

1public class SpiTester {
2 public static void main(String[] args) throws Exception{
3 ExtensionLoader<Filter> filterExtensionLoader = ExtensionLoader.getExtensionLoader(Filter.class);
4 Set<String> supportedExtensions = filterExtensionLoader.getSupportedExtensions();
5 System.out.println(supportedExtensions);
6 //[accesslog, activelimit, cache...]
7 Filter hello = filterExtensionLoader.getExtension("hello");
8 //com.github.easylog.spi.ProviderHelloFilter@299a06ac
9 System.out.println(hello);
10 }
11
12}**那么这种思想你学会了吗? **
最后求关注,求订阅,谢谢你的阅读!
