SPI机制

根本概述

SPI 全称 Service Provider Interface ,是一种服务发现机制。通过提供接口、预约义的加载器( Loader )以及约定俗称的配置(个别在 META-INF 目录下),能够实现动静加载服务实现类。

类图

通过类图能够剖析出, ServiceLoader 实现了 Iterable 接口,提供了迭代的性能。

ServiceLoader 将迭代的实现委托给 LazyIterator

LazyIterator 提供了延时迭代的能力,当有须要的时候,才去加载。

Skywalking 模块中的应用

接口定义

org.apache.skywalking.oap.server.library.module.ModuleDefine

package org.apache.skywalking.oap.server.library.module;import java.lang.reflect.Field;import java.util.Enumeration;import java.util.Properties;import java.util.ServiceLoader;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * A module definition. */public abstract class ModuleDefine implements ModuleProviderHolder {    private static final Logger LOGGER = LoggerFactory.getLogger(ModuleDefine.class);    private ModuleProvider loadedProvider = null;    private final String name;    public ModuleDefine(String name) {        this.name = name;    }    /**     * @return the module name     *     */    public final String name() {        return name;    }    /**     * @return the {@link Service} provided by this module.     */    public abstract Class[] services();    /**     * Run the prepare stage for the module, including finding all potential providers, and asking them to prepare.     *     * @param moduleManager of this module     * @param configuration of this module     * @throws ProviderNotFoundException when even don't find a single one providers.     */    void prepare(ModuleManager moduleManager, ApplicationConfiguration.ModuleConfiguration configuration,        ServiceLoader<ModuleProvider> moduleProviderLoader) throws ProviderNotFoundException, ServiceNotProvidedException, ModuleConfigException, ModuleStartException {        // etc...    }        // etc...    @Override    public final ModuleProvider provider() throws DuplicateProviderException, ProviderNotFoundException {        if (loadedProvider == null) {            throw new ProviderNotFoundException("There is no module provider in " + this.name() + " module!");        }        return loadedProvider;    }}

接口实现

org.apache.skywalking.oap.server.library.module.BaseModuleA

package org.apache.skywalking.oap.server.library.module;public class BaseModuleA extends ModuleDefine {    public BaseModuleA() {        super("BaseA");    }    // 须要提供服务的接口    @Override    public Class<? extends Service>[] services() {        return new Class[] {            ServiceABusiness1.class,            ServiceABusiness2.class        };    }    public interface ServiceABusiness1 extends Service {        void print();    }    public interface ServiceABusiness2 extends Service {    }}

META-INF 定义

META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine

org.apache.skywalking.oap.server.library.module.BaseModuleA

应用形式

org.apache.skywalking.oap.server.library.module.ModuleManager#init

    /**     * Init the given modules     */    public void init(ApplicationConfiguration applicationConfiguration) /* etc... */ {        // SPI机制加载        ServiceLoaderModuleDefine> moduleServiceLoader = ServiceLoader.load(ModuleDefine.class);        // 迭代器获取        for (ModuleDefine module : moduleServiceLoader) {            // do something            // etc...        }        // etc...    }

源码解析

package java.util;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.URL;import java.security.AccessController;import java.security.AccessControlContext;import java.security.PrivilegedAction;import java.util.ArrayList;import java.util.Enumeration;import java.util.Iterator;import java.util.List;import java.util.NoSuchElementException;public final class ServiceLoader<S> implements Iterable<S> {    // 目录前缀    private static final String PREFIX = "META-INF/services/";    // 须要被加载对象的Class对象    private final Class<S> service;    // 类加载器    private final ClassLoader loader;    // The access control context taken when the ServiceLoader is created    private final AccessControlContext acc;    // 加载对象缓存(按实例化程序排序)    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();    // 以后应用的懒加载迭代器    private LazyIterator lookupIterator;    // 重载    public void reload() {        // 革除加载对象缓存        providers.clear();        // 重置懒加载迭代器        lookupIterator = new LazyIterator(service, loader);    }    // 不容许间接创立ServiceLoader对象,只能通过loadXXX获取ServiceLoader对象    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();    }    private static void fail(Class<?> service, String msg, Throwable cause) throws ServiceConfigurationError {        throw new ServiceConfigurationError(service.getName() + ": " + msg, cause);    }    private static void fail(Class<?> service, String msg) throws ServiceConfigurationError {        throw new ServiceConfigurationError(service.getName() + ": " + msg);    }    private static void fail(Class<?> service, URL u, int line, String msg) throws ServiceConfigurationError {        fail(service, u + ":" + line + ": " + msg);    }    // 解析配置文件中的一行,如果没有正文,则退出到类名列表中    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) {            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);            }            if (!providers.containsKey(ln) && !names.contains(ln))                names.add(ln);        }        return lc + 1;    }    // 解析配置文件,返回实现类名列表    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();            r = new BufferedReader(new InputStreamReader(in, "utf-8"));            int lc = 1;            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);            }        }        return names.iterator();    }    // 懒加载迭代器,提供了延时迭代的能力,当有须要的时候,才去加载    private class LazyIterator implements Iterator<S> {        // 须要被加载对象的Class对象        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;        }        // 是否有下一个Service        private boolean hasNextService() {            if (nextName != null) {                return true;            }            // 加载所有配置文件            if (configs == null) {                try {                    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 = pending.next();            return true;        }        // 获取下一个Service        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        }        // 迭代器,是否有下个元素        public boolean hasNext() {            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() {            if (acc == null) {                return nextService();            } else {                // 受权资源                PrivilegedAction<S> action = new PrivilegedAction<S>() {                    public S run() { return nextService(); }                };                return AccessController.doPrivileged(action, acc);            }        }        // 不反对删除        public void remove() {            throw new UnsupportedOperationException();        }    }    // 迭代器实现,如果有缓存从缓存中获取,没有则从懒加载迭代器加载    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();            }        };    }    // 通过类的Class对象及类加载,获取ServiceLoader    public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {        return new ServiceLoader<>(service, loader);    }    // 通过类的Class对象,获取ServiceLoader    public static <S> ServiceLoader<S> load(Class<S> service) {        ClassLoader cl = Thread.currentThread().getContextClassLoader();        return ServiceLoader.load(service, cl);    }    // 通过类的Class对象和扩大类加载器,获取ServiceLoader    public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {        ClassLoader cl = ClassLoader.getSystemClassLoader();        ClassLoader prev = null;        while (cl != null) {            prev = cl;            cl = cl.getParent();        }        return ServiceLoader.load(service, prev);    }    public String toString() {        return "java.util.ServiceLoader[" + service.getName() + "]";    }}

PS: JDK 提供的 SPI 机制,必须要应用迭代器遍历获取须要的实现,而 Dubbo SPI 能够通过 #getExtension 获取指定实现类。

总结

通过源码剖析,能够理解到 Skywalking 没有定义本人的 SPI 机制,但深刻浏览 Skywalking 的应用场景后,发现用 JDK 提供的 SPI 机制也没什么问题。

集体认为,任何技术都应该依据场景选取,适宜的才是最好的,如果没有那么简单的须要,没必要像 dubbo 一样,定义本人的 SPI 机制。

参考文档

  • Java Service Provider Interface
  • Dubbo SPI

    分享并记录所学所见