在 Java 中,SPI(Service Provider Interface)全称为服务提供者接口,它是一种用于实现框架扩展和插件化的机制。

一、SPI 作用

二、基本原理

三、代码样例

1、定义服务接口

假设现在有一个权威机构,比如 Java,它需要对数据存储进行规范。

它定义了一个数据存储接口,和一个加载实现类的工具类。

package com.storage.specification;

/**
 * 数据存储接口
 */
public interface StorageProvider {

    void save(String data);
}
---------------------------------------------------
package com.storage.specification.spi;

import com.storage.specification.StorageProvider;

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

/**
 * 加载 存储服务 工具
 */
public class ProviderLoader {

    public static StorageProvider getStorageProvider(ClassLoader classLoader) {
        // 从类路径 resources/META-INF/services 下加载 存储服务提供者
        ServiceLoader<StorageProvider> loader = ServiceLoader.load(StorageProvider.class, classLoader);
        Iterator<StorageProvider> iterator = loader.iterator();
        List<StorageProvider> providers = new ArrayList<>();
        while (iterator.hasNext()) {
            providers.add(iterator.next());
        }
        return providers.get(0);
    }
}

2、实现服务接口

 现在有两个供应商 Mysql 和 Redis,它们拿到 Java 给的规范,分别提供他们的 二进制文件 存储实现和内存数据库实现。

Mysql 实现:

 以同样的方式创建一个 Redis 项目,取名 redis-storage-provider

3、定义服务使用者

1)用 IDEA 新建一个 maven 项目,取名叫 storage-user

Java SPI 原理、样例-LMLPHP

2)pom 依赖引入 规范 gav 和 mysql gav

<dependencies>
        <dependency>
            <groupId>com.storage.specification</groupId>
            <artifactId>storage-specification</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.mysql.storage</groupId>
            <artifactId>mysql-storage-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>

 3) 调用 mysql 存储

Java SPI 原理、样例-LMLPHP

4)切换 redis 依赖

<dependencies>
        <dependency>
            <groupId>com.storage.specification</groupId>
            <artifactId>storage-specification</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.redis.storage</groupId>
            <artifactId>redis-storage-provider</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
</dependencies>

 5)调用 redis 存储

可以看到,我们只修改了依赖的实现包,并没有修改代码,就实现了服务的替换。

而且我们的代码中并没有显示地调用服务的实现类。

Java SPI 原理、样例-LMLPHP

四、总结

 

09-29 02:09