程序员Android 学习笔记

SPI (Service Provider Interface)

2023-02-18  本文已影响0人  mlya

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
}
上一篇下一篇

猜你喜欢

热点阅读