Java SPI机制

一、简介

A service is a well-known set of interfaces and (usually abstract) classes. A service provider is a specific implementation of a service. The classes in a provider typically implement the interfaces and subclass the classes defined in the service itself. Service providers can be installed in an implementation of the Java platform in the form of extensions, that is, jar files placed into any of the usual extension directories. Providers can also be made available by adding them to the application’s class path or by some other platform-specific means.

SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,旨在由第三方实现或扩展的API 。它可用于启用框架扩展和可替换组件。

二、使用

spi 的使用场景,一般是提供一个可由第三方扩展的服务接口。并提供加载机制。(1-2,一般是一个基础jar包)
用户可以扩展自定义服务的具体实现,注册后,底层的加载机制就可以加载出来使用了。(3-4,一般是写在扩展 jar 包)
比如以模拟 java.jdbc.Driver 为例:
在这里插入图片描述

1、定义服务接口
package com.c.service;

public interface Driver {

    String getName();

    void connect();

    void disConnect();

}

2、实现服务加载以及使用类

JDK 中查找服务的实现的工具类是:java.util.ServiceLoader

package com.c.service;

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

/**
 * @author c
 */
public class Connector {
    private static final List<Driver> drivers = new ArrayList<>();

    @SuppressWarnings("squid:S3077")
    private static volatile Connector instance;

    private Connector() {
        init();
    }

    private void init() {
        // SPIUtils.foreachRemaining(Driver.class, drivers::add);

        // 查找 服务实现类
        ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
        Iterator<Driver> iterator = loader.iterator();
        while (iterator.hasNext()) {
            try {
                Driver next = iterator.next();
                if (next == null) {
                    continue;
                }

                drivers.add(next);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static Connector getInstance() {
        if (instance == null) {
            synchronized (Connector.class) {
                if (instance == null) {
                    instance = new Connector();
                }
            }
        }
        return instance;
    }

    public Driver[] getDrivers() {
        return drivers.toArray(new Driver[0]);
    }

}

3、实现服务接口
package com.extension.service.impl;

import com.c.service.Driver;

/**
 * @author c
 */
public class MySQLDriver implements Driver {


    @Override
    public String getName() {
        return "mysql";
    }

    @Override
    public void connect() {
        System.out.println("连接到 mysql");
    }

    @Override
    public void disConnect() {
        System.out.println("关闭 mysql 连接");
    }
}

package com.extension.service.impl;

import com.c.service.Driver;

/**
 * @author c
 */
public class OracleDriver implements Driver {
    @Override
    public String getName() {
        return "oracle";
    }

    @Override
    public void connect() {
        System.out.println("连接到 oracle");
    }

    @Override
    public void disConnect() {
        System.out.println("关闭 oracle 连接");
    }
}

4、注册实现类

当服务的提供者提供了一种接口的实现之后,需要在 cp 目录下的 META-INF/services/ 目录创建一个以服务接口全限定类名命名的文件。这个文件里的内容就是这个服务接口的实现类。当其他程序需要这个服务的时候,就可以通过查找 META-INF/services/ 里配置文件中的实现类名,实例化服务对象。就可以使用该服务了。

在这里插入图片描述

com.extension.service.impl.MySQLDriver
com.extension.service.impl.OracleDriver
5、使用
    public static void main(String[] args) {
        Driver[] drivers = Connector.getInstance().getDrivers();

        for (Driver driver : drivers) {
            System.out.println("===== 加载到 Driver " + driver.getName() + "=====");
            driver.connect();
            driver.disConnect();
        }

    }

输出:

===== 加载到 Driver...mysql=====
连接到 mysql
关闭 mysql 连接
===== 加载到 Driver...oracle=====
连接到 oracle
关闭 oracle 连接

三、封装一个 SPI 加载的工具类

import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.function.Consumer;

public class SPIUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(SPIUtils.class);

    private SPIUtils() {}

    public static <T> void foreachRemaining(Class<T> clazz, Consumer<? super T> action) {
        if (action == null) {
            return;
        }

        ServiceLoader<T> loader = ServiceLoader.load(clazz);
        Iterator<T> iterator = loader.iterator();
        while (iterator.hasNext()) {
            try {
                T next = iterator.next();
                if (next == null) {
                    continue;
                }

                action.accept(next);
            } catch (Exception e) {
                LOGGER.warn(e.getMessage(), e);
            }
        }
    }

}

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非理性地界生物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值