Java 服务提供者接口

  • 什么是java Spi
  • SPI组件构成
  • API和SPI区别
  • SPI实战演练

什么是java Spi

SPI: 用于发现和加载与给定接口匹配的实现。服务提供者接口:(供服务的提供者或者框架扩展者去实现服务的一套接口标准)

SPI组件构成

ServiceLoader

SPI 的核心是 ServiceLoader 类。加载服务实现的类。具有延迟发现和加载实现的作用。 它使用上下文类路径来定位提供者实现并将它们放入内部缓存中即 通过读取服务提供者配置的META-INF/services下的文件信息获取对应服务提供者接口的实现类。

Service(服务)

一组众所周知的编程接口和类,存在零个、一个或多个服务提供者。它们提供对某些特定应用程序功能或特性的访问。

Service Provider Interface(服务提供者接口)

为了实现特定应用功能的接入,制定的一套标准化的接口,被用于服务提供者去实现该接口,完成对功能的实现。 一般为具体的sdk服务厂家提供的接口标准。(jdk 为 oracle 驱动提供的接口)

Service Provider (服务提供者(实现这个服务的具体模块或者类))

实现提供者接口的具体实现。通过配置 META-INF/services下的文件来确定服务器提供者接口和服务提供者实现。

API和SPI区别

  • 当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API ,这种接口和实现都是放在实现方的。
  • 当接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务。

Java 生态系统中的 SPI 示例

SPI实战演练

  • 这里我们通过日志类型进行项目演示
  • 我们首先创建一个名为service-provider-interface的 Maven 项目
  • 然后我们创建一个Logger接口用于日志输出
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.ahut.spi;

/**
 * 日志接口定义相关日志信息输出
 * @author Cherubr
 * 通用服务用于日志输出
 */
public interface Logger {
    /**
     * 输出日志
     * @param message
     */
    void write(String message);
}
  • 然后我们创建服务提供者用于制定创建那种日志输出
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.ahut.spi;

/**
 * @author Sumin.G
 * @title: Logger
 * @projectName java-base
 * @description: SPI 接口提供者 用于服务提供者去实现该接口,完成对功能的实现
 * @date 2022/9/69:40
 */
public interface LoggerProvider {
    /**
     * 创建日志管理类
     * @return
     */
    Logger create();
}
  • 最后是我们的serviceloader类加载对应接口
 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
package com.ahut.spi.impl;

import com.ahut.spi.LoggerProvider;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

/**
 * @author Sumin.G
 * @title: Logger
 * @projectName java-base
 * @description: TODO
 * @date 2022/9/69:40
 */
public class LoggerManager {

    private static final LoggerManager SERVICE = new LoggerManager();


    public static List<LoggerProvider> loggerList;

    private LoggerManager() {
        ServiceLoader<LoggerProvider> loader = ServiceLoader.load(LoggerProvider.class);
        List<LoggerProvider> list = new ArrayList<>();
        for (LoggerProvider log : loader) {
            list.add(log);
        }
        loggerList = list;
    }

    public static LoggerManager getService() {
        return SERVICE;
    }


}

到这里用户的服务提供者完成。 SPI_serverProvider.png

  • 下面我们创建服务提供者实现 创建一个名为service-provider的 Maven 项目,并将 service-provider-interface 依赖项添加到pom.xml 创建SPI实现类 用于构建对应Logger 日志具体实现类
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.ahut.spi.server;

import com.ahut.spi.Logger;
import com.ahut.spi.LoggerProvider;

/**
 * @author Sumin.G
 * @title: LogbackProvider
 * @projectName java-base
 * @description: TODO
 * @date 2022/9/617:16
 */
public class LogbackProvider implements LoggerProvider {
    @Override
    public Logger create() {
        return new Logback();
    }
}
  • 然后是日志具体输出实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package com.ahut.spi.server;

import com.ahut.spi.Logger;

/**
 * @author Sumin.G
 * @title: Logback
 * @projectName java-base
 * @description: TODO
 * @date 2022/9/610:16
 */
public class Logback implements Logger {

    @Override
    public void write(String message) {
        System.out.println("logback输出日志 "+message);
    }
}

为了被发现,我们创建了一个提供者配置文件 META-INF/services/com.ahut.spi.LoggerProvider –>文件内容为实现类名称 com.ahut.spi.server.LogbackProvider SpiProvider.png

  • 新建APP类Main
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ahut;

import com.ahut.spi.impl.LoggerManager;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        testSpi();
    }

    public static void testSpi(){
       LoggerManager.loggerList.stream().forEach(e->{
           e.create().write("123");
       });
    }
}
1
输出结果: logback输出日志 123