JAVA SPI 介绍

1 JAVA SPI

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

使用示例:

  1. 定义接口
public interface Operation {
    void saySomething();
}

2.定义实现类(可以是其他jar包,不同的jar包有不同的实现,比如jdbc驱动)

public class OperationImplA implements Operation{
    @Override
    public void saySomething() {
        System.out.println("Hello A");
    }
}

public class OperationImplB implements Operation{
    @Override
    public void saySomething() {
        System.out.println("Hello B");
    }
}

  1. 在META-INF/services下创建实现类记录
    META-INF/services 文件夹下创建一个文件,名称为接口的路径名,内容位对应的实现类
    在这里插入图片描述

  2. 测试调用

    @Test
    void operationTest() {
        ServiceLoader<Operation> op = ServiceLoader.load(Operation.class);
        op.forEach(Operation::saySomething);
    }

源码分析:

ServiceLoader.load(Operation.class)

    // 1. 获取类加载器,使用类加载器和要加载的实现类创建服务加载对象
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

    // 2. 构造函数为私有,设置service和loader后,执行reload
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }

    // 缓存已加载的实现类ServiceLoader的内部类,用于懒加载
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // LazyIterator是
    private LazyIterator lookupIterator;
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
	private class LazyIterator
        implements Iterator<S>
    {

        Class<S> service;  // 要加载的服务,即接口
        ClassLoader loader; // 使用的类加载器
        Enumeration<URL> configs = null; // 加载的文件集合
        Iterator<String> pending = null; //当前正在加载的文件的输出流迭代器
        String nextName = null; //下一个实现类的全路径名

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            
            // 第一次执行时,configs为空
            if (configs == null) {
                try {
                    // fullName为要加载的文件名,PREFIX为META-INF/services
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            
            //pending为空或者pending已经读取完毕,获取下一个pending
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        // 直接加载nextName,并将nextName清空,下次调用hasNextService时会重新设置
        private S nextService() {
            if (!hasNextService())
                throw new NoSuchElementException();
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     "Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service,
                     "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }

        //... 省略部分

    }
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值