java-jdk-spi

87 阅读8分钟

SPI

service provider interface,JDK内置提供的服务发现/加载机制;实现基于接口的编程模型; 实现定制化服务(可插拔加载不同的实现,通过jar内的配置文件(META-INF/services)实现策略调度)

SPI usage

  • META-INF/services/文件名是接口的全名; 文件内容是实现类的全名; 行注释使用#
  • 实现类在jar中时,Jar路径包要设置在classpath下; 只需要配置maven,就是指定实现
  • 实现类需要有一个不带参数的构造函数, 用来clazz.newInstance()创建实例对象
  • 使用ServiceLoader.load() 加载实现类(默认使用当前线程的classLoader)

缺陷: 如果加载了全部的实现,不方便调用指定的某一个实现...

应用实例

common-logging

在早期的jar中配置文件META-INF/services/org.apache.commons.logging.LogFactory;实现日志门面接口,由各自的日志提供商自动实现,定制化实现

jdbc

不用再使用Class.forName()加载指定的驱动类,直接maven引入就可以;查看mysql-connector-java的jar的源文件

dubbo源码应用解析

dubbo-spi在jdk-spi的基础上进行加强,

  • 实现动态根据key加载指定的实现,adaptive自适应
  • 通过ios和aop实现依赖扩展和自动加载;
  • Dubbo-spi在实现上引入了本地缓存和线程安全的考虑(loader是单例),牺牲内存但性能更高;
  • 使用了代理设计模式实现了更加灵活的加载,例如Wrapper和依赖扩张的运行时加载...是dubbo模块的核心技术.

motan源码应用分析

self code

自己继承是实现ExtensionLoader();学习dubbo

jdk-spi的源码分析

// SPI的实现类,通过继承本能的支持iterator相关操作
// 只保留了关键的代码
public final class ServiceLoader<S> implements Iterable<S>
{
    private static final String PREFIX = "META-INF/services/";
    // interface
    private final Class<S> service;
    private final ClassLoader loader;
    // 访问控制,不懂可以忽略
    private final AccessControlContext acc;
    // 缓存已经实例化的实现
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    // The current lazy-lookup iterator, 延迟迭代的内部类
    // 控制延迟加载流程的核心内部类
    private LazyIterator lookupIterator;

    // 模式使用当前线程的classloader加载服务
    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);
    }
    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();
    }
    //  使用了延迟迭代完成加载, 在程序运行的过程中加载
    public void reload() {
        providers.clear();
        lookupIterator = new LazyIterator(service, loader);
    }

    // 通过私有的内部类实现了 延迟的加载, 控制了延迟加载的流程
    // 实现了加载的主要流程
    // 增加了hasNextService(),nextService()控制主要的延迟加载的流程
    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;
            }
            // 加载所有的配置文件
            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);
                }
            }
            // 到此,jar内的所有配置文件全部加载完成
            // 依次加载每个配置文件
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                // 每次顺序加载一个配置文件就可以
                pending = parse(service, configs.nextElement());
            }
            // 返回下一个实现类名
            nextName = pending.next();
            return true;
        }

        // 延迟的核心实现
        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);
            }
            // 卧槽...这是JVM异常
            throw new Error();          // This cannot happen
        }

        public boolean hasNext() {
            // 封装了权限的判断,底层是自己实现的hasNextService
            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() {
            // 封装了权限的判断,底层是自己实现的nextService
            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();
        }
    }
     // 加载某个配置文件下的所有实现类
    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();
    }

    // 加载这个文件到List(names), 返回加载的个数
    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;
        }
        // 处理comment
        int ci = ln.indexOf('#');
        if (ci >= 0) ln = ln.substring(0, ci);
        ln = ln.trim();
        int n = ln.length();
        if (n != 0) {
            //  配置失败直接Exception
            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 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);
    }


    // Lazily loads the available providers of this loader's service.
    // 使用了 providers,lookupIterator 返会自己的迭代器, 可以访问全部的实现类
    public Iterator<S> iterator() {
        return new Iterator<S>() {
            // 已经加载的缓存
            Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
            public boolean hasNext() {
                // 1.先判断缓存
                if (knownProviders.hasNext())
                    return true;
                    // 2. 缓存没有则动态的加载
                return lookupIterator.hasNext();
            }
            public S next() {
                // 1. 先判断缓存
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                    // 2,再动态判断加载
                return lookupIterator.next();
            }
            // 当然不能删除,这里只提供迭代作用
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    // 使用jdk的classloader加载服务
    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);
    }
}

ServiceLoader不是在一开始实例化后就加载全部的实现,是通过内部类LazyIterator实现的延迟加载;

延迟加载的规则是,每次先加载一个文件,依次返回文件的一行(一个实现类)用于实例化服务类;一个文件遍历完成后再加载另一个文件,都是顺序加载

缺点: 会加载全部的服务实现,运行过程中不能指定运行某一个具体的服务实现类


public class JavaSPITest {
    @Test
    public void sayHello() throws Exception {
        // 1. 会一次加载全部的服务实现类
        ServiceLoader<Robot> robotServiceLoader = ServiceLoader.load(Robot.class);
        System.out.println("Java SPI");
        // 2. 没法选择指定的实现
        // robotServiceLoader.iterator().forEachRemaining(Robot::sayHello);
        //robotServiceLoader.forEach(Robot::sayHello);
        //robotServiceLoader.reload();
        // 3. 通过代码实现默认调用第一个实现,算是一种折中的解决方案
        Robot first = robotServiceLoader.iterator().next();
        first.sayHello();
    }
}

模拟Dubbo实现简单的SPI,实现功能增强

实现了本地缓存 和 线程安全; 通过注解指定记载; 还可以继续加强

/**
 * simple servier loader 对jdk-spi的服务增强
 * 主要模仿dubbo-spi进行增强
 * <p>
 * 加强的规则
 * 1. @SimpleSPI注解注解接口(指定默认的实现和实例的类型:单例/原型)
 * 2. @SimpleSPIName注解指定实现类的Name,默认通过name加载指定的实现
 * 3. 兼容jdk-spi
 * 4. 1+2 的功能也可以通过配置文件实现
 */
// 每个loader是单例,需要处理线程安全的问题
public class SimpleServiceLoader<T> {

    private static final Logger log = LoggerFactory.getLogger(SimpleServiceLoader.class);

    // 支持扫描jdk-spi的目录
    private static final String PREFIX = "META-INF/services/";
    private static final String PREFIX_SIMPLE = PREFIX + "simple/";

    // 使用类缓存,缓存所有的需要加载的接口,不需要重复加载
    // 使用类的缓存应该是大写区分实例的缓存
    private static final ConcurrentMap<Class<?>, SimpleServiceLoader<?>> CACHED_SERVICE_LOADER = new ConcurrentHashMap<Class<?>, SimpleServiceLoader<?>>();

    // 使用实例缓存,type接口的所有实例缓存
    private final ConcurrentMap<String, VolatileHolder<Object>> cachedInstances = new ConcurrentHashMap<String, VolatileHolder<Object>>();
    // Type接口需要加载的全部实现类名缓存
    private final VolatileHolder<Map<String, Class<?>>> cachedClasses = new VolatileHolder<Map<String, Class<?>>>();

    // 接口,默认的实现
    private final Class<T> type;
    private final String cachedDefaultInstance;

    public static <T> SimpleServiceLoader<T> getSimpleServiceLoader(Class<T> clz) {
        if (clz == null) {
            throw new IllegalArgumentException("service type == null");
        }
        if (!clz.isInterface()) {
            throw new IllegalArgumentException("Service type (" + clz.getName() + ") is not interface");
        }
        // 兼容JDK-SPI 注解不是必须的
        // if (!clz.isAnnotationPresent(SimpleSPI.class)) { }
        SimpleServiceLoader<T> loader = (SimpleServiceLoader<T>) CACHED_SERVICE_LOADER.get(clz);
        if (loader == null) {
            CACHED_SERVICE_LOADER.putIfAbsent(clz, new SimpleServiceLoader<T>(clz));
            loader = (SimpleServiceLoader<T>) CACHED_SERVICE_LOADER.get(clz);
        }
        return loader;
    }

    // 接口对应的loader实例
    private SimpleServiceLoader(Class<T> type) {
        this.type = type;
        SimpleSPI simpleSPI = type.getAnnotation(SimpleSPI.class);
        if (simpleSPI != null) {
            cachedDefaultInstance = simpleSPI.value();
        } else {
            // 默认为空
            cachedDefaultInstance = null;
        }
    }

    private ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    public T getService(String name) {
        // 判断默认输入
        if (name == null || name.length() == 0) {
            // 加载默认
            return getDefaultService();
        }

        // 处理多线程并发的范式
        // concurrenthashMap能解决多线程访问的效率问题,没法处理锁的问题
        VolatileHolder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new VolatileHolder<Object>());
            holder = cachedInstances.get(name);
        }

        // 处理线程安全问题
        Object instance = holder.getValue();
        if (instance == null) {
            // 最小粒度加锁,不对当前loader实例加锁,因为loader加锁的粒度太大
            // 对name加锁, 但是name是入参
            // 对intance加锁, 但是instance是null
            synchronized (holder) {
                instance = holder.getValue();
                if (instance == null) {
                    instance = createService(name);
                    holder.setValue(instance);
                }
            }
        }
        return (T) instance;
    }

    public T getDefaultService() {
        if (cachedDefaultInstance == null || cachedDefaultInstance.length() == 0) {
            return null;
        }
        return getService(cachedDefaultInstance);
    }

    // 创建实例Instance
    private T createService(String name) {
        // 所有的实现类
        Class clz = getServiceClass(name);
        if (clz == null) {
            throw new IllegalArgumentException("service instance(" + name + ") not exists.");
        }
        // 直接创建实例,已经在实例层加锁
        try {
            Object instance = clz.newInstance();
            return (T) instance;
        } catch (Exception e) {
            throw new IllegalArgumentException("create Service instance(" + name + ") error");
        }
    }

    // 获取所有的实现的类名
    // 接口调用一次加载接口的全部实现
    private Class<?> getServiceClass(String name) {
        Map<String, Class<?>> classMap = cachedClasses.getValue();
        // 这个加锁
        if (classMap == null) {
            // 一次需要加载全部的实现,所以对整个缓存加锁,保证cachedClasses不是null, 且多线程可见
            synchronized (cachedClasses) {
                classMap = cachedClasses.getValue();
                if (classMap == null) {
                    Map<String, Class<?>> tmpMap = loadServices();
                    cachedClasses.setValue(tmpMap);
                }
            }
        }
        return cachedClasses.getValue().get(name);
    }

    // 加载配置
    private Map<String, Class<?>> loadServices() {
        Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
        loadDir(classMap, PREFIX);
        loadDir(classMap, PREFIX_SIMPLE);
        return classMap;
    }

    // 加载配置的目录
    private void loadDir(Map<String, Class<?>> classMap, String path) {
        String fileName = path + type.getName();
        log.info("loadDir:{}", fileName);
        try {
            // 目录下所有的同名文件
            Enumeration<URL> resources;
            ClassLoader classloader = getClassLoader();
            if (classloader != null) {
                resources = classloader.getResources(fileName);
            } else {
                resources = ClassLoader.getSystemResources(fileName);
            }
            // 依次加载每个文件
            if (resources != null) {
                while (resources.hasMoreElements()) {
                    loadFile(classMap, classloader, resources.nextElement());
                }
            }
        } catch (Exception e) {
            log.error("loadDir:", e);
        }
    }

    // 加载配置文件
    private void loadFile(Map<String, Class<?>> classMap, ClassLoader classloader, URL url) {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
            String line;
            while ((line = br.readLine()) != null) {
                //  注释简单的处理
                if (line.indexOf("#") >= 0) {
                    continue;
                }
                Class<?> clz = Class.forName(line, true, classloader);
                String name = clz.getSimpleName();
                if (!type.isAssignableFrom(clz)) {
                    throw new IllegalArgumentException("class(" + clz.getName() + ") is not subclass of " + type);
                }
                // 实现类的name
                if (!clz.isAnnotationPresent(SimpleSPIName.class)) {
                    SimpleSPIName spiName = clz.getAnnotation(SimpleSPIName.class);
                    name = spiName.name();
                }
                // 更新name格式
                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                classMap.put(name, clz);
            }
            // 应该finally
            br.close();
        } catch (Exception e) {
            log.error("loadFile", e);
        }
    }

    // 测试
    public static void main(String[] args) {
        // 95a2dc313f6f49d166016c4ad492577e1d297ca68e10058f9135c2981f5fc760
        SimpleServiceLoader<Robot> loader = SimpleServiceLoader.getSimpleServiceLoader(Robot.class);
        // 默认调用
        Robot defaultService = loader.getDefaultService();
        defaultService.sayHello();
        // 指定调用
        Robot optimusPrime = loader.getService("optimusPrime");
        optimusPrime.sayHello();
    }
}