Java 代码之 SPI

1. wiki

SPI(Service Provider Interface),是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类。

2. 用例

  1. 定义一个接口,用来被引用

    1
    2
    3
    4
    package com.spiDemo.spi;
    public interface SPIService {
    void execute();
    }
  2. 创建两个实现类

    1
    2
    3
    4
    5
    6
    7
    package com.spiDemo;
    public class SpiImpl1 implements SPIService {
    @Override
    public void execute() {
    System.out.println("SpiImpl1.execute()");
    }
    }
1
2
3
4
5
6
7
package com.spiDemo;
public class SpiImpl2 implements SPIService {
@Override
public void execute() {
System.out.println("SpiImpl2.execute()");
}
}
  1. 在 ClassPath 路径下配置添加一个文件,文件名是接口的全限定类名,内容是实现类的全限定类名,多个实现类用换行符分隔

    1
    2
    3
    4
    5
    6
    # 位置
    src/main/resources/META-INF/com.spiDemo.SPIService

    # 内容
    com.spiDemo.SpiImpl1
    com.spiDemo.SpiImpl2
  2. 调用可以通过 ServiceLoader.load()或者Service.providers()方法拿到实现类的实例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.spiDemo;

    import java.util.Iterator;
    import java.util.ServiceLoader;
    import sun.misc.Service;

    public class DBDriverDemo {
    public static void main(String[] args) {
    System.out.println("--------------------------------");
    // 方法一
    Iterator<SPIService> providers = Service.providers(SPIService.class);
    while (providers.hasNext()) {
    SPIService ser = providers.next();
    ser.execute();
    }
    // 方法二
    // ServiceLoader<SPIService> serviceLoader = ServiceLoader.load(SPIService.class);
    // for (SPIService sPIService : serviceLoader){
    // ...
    // }

    System.out.println("--------------------------------");
    ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
    Iterator<SPIService> iterator = load.iterator();
    while (iterator.hasNext()) {
    SPIService ser = iterator.next();
    ser.execute();
    }
    }
    }

    # 输出
    --------------------------------
    SpiImpl1.execute()
    SpiImpl2.execute()
    --------------------------------
    SpiImpl1.execute()
    SpiImpl2.execute()

3. 注意

  1. 严格按照约定的文件路径和文件名来命名,”META-INF/services/“

4. 参考

深入理解SPI机制