美文网首页程序员Android 学习笔记
SPI (Service Provider Interface)

SPI (Service Provider Interface)

作者: mlya | 来源:发表于2023-02-18 18:26 被阅读0次

    SPI(Service Provider Interface),是 JDK 内置的一种服务提供发现机制,是一种策略模式的实现方式。

    通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类。

    SPI 基础使用

    我们在 java 同级的目录下,创建 META-INF/services 目录,然后以接口的全限定名创建文件:com.example.spidemo.SPIService

    image-20230219171635202.png

    文件中定义了接口的具体实现类:

    com.example.spidemo.SpiImpl1
    com.example.spidemo.SpiImpl2
    com.example.spidemo.SpiImpl3
    

    那么,我们就可以使用 ServiceLoader 获取到这个 SPIService 的具体实现了。

    public class SpiDemoTest {
        @Test
      
        public void test() {
            ServiceLoader<SPIService> loader = ServiceLoader.load(SPIService.class);
            for (SPIService spiService : loader) {
                spiService.execute();
            }
        }
    }
    

    源码分析

    ServiceLoader.load() 的执行过程

    核心是,创建了一个 LazyIetrator 对象,真正通过反射创建实现类的方法不在这里。

    // 我们没传入 classloader,获取当前线程的 classloader  
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    
    // new 出来一个 ServiceLoader,两个参数:接口名 & classloader
    public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
    {
        return new ServiceLoader<>(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;
        // Android-changed: Do not use legacy security code.
        // On Android, System.getSecurityManager() is always null.
        // acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }
    
    // 这里 new 出来一个 LazyIterator
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }
    
    // LazyIterator 只是存储了两个参数,没有实际的逻辑
    private LazyIterator(Class<S> service, ClassLoader loader) {
        this.service = service;
        this.loader = loader;
    }
    

    缓存机制

    ServiceLoader 的 iterator 方法实现如下,这里实现了一个简单的缓存机制。

    knownProviders 是缓存 providers 的迭代器,在遍历时,会优先从 knownProviders 中读取缓存,如果读到了,就直接返回;如果没读到,再从 loopupIterator 中去读取。

    loopupIterator 就是上面的 LazyIterator

    // ServiceLoader.java
    public Iterator<S> iterator() {
        return new Iterator<S>() {
    
            Iterator<Map.Entry<String,S>> knownProviders
                = providers.entrySet().iterator();
    
            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }
    
            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }
    
            public void remove() {
                throw new UnsupportedOperationException();
            }
    
        };
    }
    

    LazyIterator 的遍历过程

    // LazyIterator 本身实现了 Iterator 接口
    private class LazyIterator
        implements Iterator<S>
    {}
          
    // 其 hasNext 方法和 next 方法分别调用了 `hasNextService` 方法和 `next` 方法
    public boolean hasNext() {
        // Android-changed: do not use legacy security code
        /* if (acc == null) { */
            return hasNextService();
        /*
        } else {
            PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                public Boolean run() { return hasNextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
        */
    }
    
    public S next() {
        // Android-changed: do not use legacy security code
        /* if (acc == null) { */
            return nextService();
        /*
        } else {
            PrivilegedAction<S> action = new PrivilegedAction<S>() {
                public S run() { return nextService(); }
            };
            return AccessController.doPrivileged(action, acc);
        }
        */
    }
    

    hasNextService 方法实现

    private boolean hasNextService() {
        // 如果已经有缓存的 nextName 变量,那么直接返回 true
        if (nextName != null) {
            return true;
        }
        // 第一次读取文件的过程
        if (configs == null) {
            try {
                // 读取配置文件
                // 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);
            }
        }
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return false;
            }
            // 解析
            pending = parse(service, configs.nextElement());
        }
        // 这里会将下一个类名存储下来,存储到 nextName 变量中
        nextName = pending.next();
        return true;
    }
    

    文件解析的实现

    private Iterator<String> parse(Class<?> service, URL u)
        throws ServiceConfigurationError
    {
        InputStream in = null;
        BufferedReader r = null;
        ArrayList<String> names = new ArrayList<>();
        try {
            // 打开本地文件
            in = u.openStream();
            // 创建 BufferedReader
            r = new BufferedReader(new InputStreamReader(in, "utf-8"));
            int lc = 1;
            // 解析每一行内容,放入 names 数组中
            while ((lc = parseLine(service, u, r, lc, names)) >= 0);
        } catch (IOException x) {
            fail(service, "Error reading configuration file", x);
        } finally {
            try {
                if (r != null) r.close();
                if (in != null) in.close();
            } catch (IOException y) {
                fail(service, "Error closing configuration file", y);
            }
        }
        // 返回 names 数组的迭代器
        return names.iterator();
    }
    
    // 解析每一行内容的代码:
    private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,
                          List<String> names)
        throws IOException, ServiceConfigurationError
    {
        // 读取到每一行内容
        String ln = r.readLine();
        if (ln == null) {
            return -1;
        }
        // 取第一个 「#」 之前的内容
        int ci = ln.indexOf('#');
        if (ci >= 0) ln = ln.substring(0, ci);
        ln = ln.trim();
        int n = ln.length();
        if (n != 0) {
            // 异常检查:不能包含空格和 '\t'
            if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
                fail(service, u, lc, "Illegal configuration-file syntax");
            // 异常检查:检查每一个字符是否都合法
            int cp = ln.codePointAt(0);
            if (!Character.isJavaIdentifierStart(cp))
                fail(service, u, lc, "Illegal provider-class name: " + ln);
            for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
                cp = ln.codePointAt(i);
                if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
                    fail(service, u, lc, "Illegal provider-class name: " + ln);
            }
            // 将类名添加到 names 数组中
            if (!providers.containsKey(ln) && !names.contains(ln))
                names.add(ln);
        }
        // 返回下一行行号
        return lc + 1;
    }
    

    nextService 实现

    private S nextService() {
        if (!hasNextService())
            throw new NoSuchElementException();
        // 将 nextName 读取到临时变量
        String cn = nextName;
        // 读取后,清楚 nextName 的值
        nextName = null;
        Class<?> c = null;
        try {
            // 通过反射,找到 cn 对应的 class 对象
            c = Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            fail(service,
                 // Android-changed: Let the ServiceConfigurationError have a cause.
                 "Provider " + cn + " not found", x);
                 // "Provider " + cn + " not found");
        }
        // 判断 c 这个类是不是 service 接口的一个实现类,如果不是,报错
        if (!service.isAssignableFrom(c)) {
            // Android-changed: Let the ServiceConfigurationError have a cause.
            ClassCastException cce = new ClassCastException(
                    service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
            fail(service,
                 "Provider " + cn  + " not a subtype", cce);
            // fail(service,
            //        "Provider " + cn  + " not a subtype");
        }
        try {
            // 实例化实现对象,并强转成 service 类型
            // 这里 S 是泛型,其实指的就是 service 的类型
            S p = service.cast(c.newInstance());
            // 缓存到 LinkedHashMap 中
            providers.put(cn, p);
            // 返回
            return p;
        } catch (Throwable x) {
            fail(service,
                 "Provider " + cn + " could not be instantiated",
                 x);
        }
        throw new Error();          // This cannot happen
    }
    

    相关文章

      网友评论

        本文标题:SPI (Service Provider Interface)

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