美文网首页
Java-SPI-ServiceLoader

Java-SPI-ServiceLoader

作者: 张明学 | 来源:发表于2020-09-02 23:31 被阅读0次

    什么是SPI?

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。有一个接口,有多个实现,通过配置来获取接口的实现(服务发现)

    Java SPI 应用实例

    当服务的提供者提供了一种接口的实现之后,需要在classpath下的META-INF/services/目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务实现的工具类是:java.util.ServiceLoader。

    示例

    SPI接口

    package com.zmx.study.eg9;
    
    /**
     * 一个SPI接口
     */
    public interface PayService {
        
        void pay();
    }
    

    SPI具体实现

    第一个实现

    public class AliPayService implements PayService {
    
        @Override
        public void pay() {
            System.out.println("AliPay...");
        }
    }
    

    第二个实现

    public class WxPayService implements PayService {
    
        @Override
        public void pay() {
            System.out.println("WxPay...");
        }
    }
    

    增加META-INF目录文件

    1、Resource下面创建META-INF/services 目录里创建一个以服务接口命名的文件

    配置.png

    文件名必须是SPI接口的全名称(包名.类名)

    2、在com.zmx.study.eg9.PayService文件中写入配置信息

    com.zmx.study.eg9.AliPayService
    com.zmx.study.eg9.WxPayService
    

    测试

    通过ServiceLoader获取服务

    public static void main(String[] args) {
        List<PayService> payServiceList = new ArrayList<>();
    
        ServiceLoader<PayService> serviceLoader = ServiceLoader.load(PayService.class);
        serviceLoader.forEach((service) -> {
            payServiceList.add(service);
            service.pay();
        });
    
        System.out.println("发现PayService服务个数:" + payServiceList.size());
    }
    

    实现机制:

    ServiceLoader.load最后走到了reload方法,LazyIterator实现了Iterator接口。

    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
    
    //判断有没有下一个服务
    private boolean hasNextService() {
        if (nextName != null) {
            return true;
        }
        if (configs == null) {
            try {
                // PREFIX就是一个常量:META-INF/services/
                // service.getName()就是接口的完整类名
                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);
            }
        }
        // ...
        nextName = pending.next();
        return true;
    }
    
    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        String cn = nextName;
        nextName = null;
        Class<?> c = null;
        try {
            // 根据配置信息接口实现的类名获取Class
            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
    }
    

    相关文章

      网友评论

          本文标题:Java-SPI-ServiceLoader

          本文链接:https://www.haomeiwen.com/subject/rqigsktx.html