zoukankan      html  css  js  c++  java
  • Java SPI(Service Provider Interface)

    SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个例子来说,有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。

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

    SPI实例

    定义接口

    package org.cellphone.api;
    
    public interface DataSource {
    
        String getConnection();
    }

    Oracle厂商实现接口

    package org.cellphone.oracle;
    
    import org.cellphone.api.DataSource;
    
    public class DataSourceImpl implements DataSource {
        @Override
        public String getConnection() {
            return "Oracle提供数据库连接池";
        }
    }

    并在classpath下的META-INF/services/目录下以接口全路径名定义文件:org.cellphone.api.DataSource,文件内容为:

    org.cellphone.oracle.DataSourceImpl

    MySQL厂商实现接口

    package org.cellphone.mysql;
    
    import org.cellphone.api.DataSource;
    
    public class DataSourceImpl implements DataSource {
        @Override
        public String getConnection() {
            return "MySQL提供数据库连接池";
        }
    }

    并在classpath下的META-INF/services/目录下以接口全路径名定义文件:org.cellphone.api.DataSource,文件内容为:

    org.cellphone.mysql.DataSourceImpl

    ServiceLoader.load(DataSource.class)读取厂商Oracle、MySQL提供jar包中的文件,ServiceLoader实现了Iterable接口可通过while for循环语句遍历出所有实现。

    package org.cellphone.invoker;
    
    import org.cellphone.api.DataSource;
    
    import java.util.ServiceLoader;
    
    public class Main {
        private static ServiceLoader<DataSource> loader = ServiceLoader.load(DataSource.class);
    
        public static void main(String[] args) {
            for (DataSource source : loader) {
                System.out.println(source.getConnection());
            }
        }
    }

     SPI源码解析

    // ServiceLoader实现了Iterable接口,可以遍历所有的服务实现者
    public final class ServiceLoader<S>
        implements Iterable<S>
    {
    
        // 查找配置文件的目录
        private static final String PREFIX = "META-INF/services/";
    
        // 表示要被加载的服务的类或接口
        private final Class<S> service;
    
        // 这个ClassLoader用来定位,加载,实例化服务提供者
        private final ClassLoader loader;
    
        // 访问控制上下文
        private final AccessControlContext acc;
    
        // 缓存已经被实例化的服务提供者,按照实例化的顺序存储
        private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    
        // The current lazy-lookup iterator
        private LazyIterator lookupIterator;
    
        // 重新加载,就相当于重新创建ServiceLoader了,用于新的服务提供者安装到正在运行的Java虚拟机中的情况
        public void reload() {
            // 清空缓存中所有已实例化的服务提供者
            providers.clear();
            // 新建一个迭代器,该迭代器会从头查找和实例化服务提供者
            lookupIterator = new LazyIterator(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();
        }
    
        // 解析失败处理的方法
        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('	') >= 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;
        }
    
        // 解析配置文件,解析指定的url配置文件
        // 使用parseLine方法进行解析,未被实例化的服务提供者会被保存到缓存中去
        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 inner class implementing fully-lazy provider lookup
        // 服务提供者查找的迭代器
        private class LazyIterator
            implements Iterator<S>
        {
    
            // 服务提供者接口
            Class<S> service;
            // 类加载器
            ClassLoader loader;
            // 保存实现类的url
            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);
                    }
                }
                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);
                }
                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();
                }
    
            };
        }
    
        // 为指定的服务使用指定的类加载器来创建一个ServiceLoader
        public static <S> ServiceLoader<S> load(Class<S> service,
                                                ClassLoader loader)
        {
            return new ServiceLoader<>(service, loader);
        }
    
        // 使用线程上下文的类加载器来创建ServiceLoader
        public static <S> ServiceLoader<S> load(Class<S> service) {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            return ServiceLoader.load(service, cl);
        }
    
        // 使用扩展类加载器为指定的服务创建ServiceLoader
        // 只能找到并加载已经安装到当前Java虚拟机中的服务提供者,应用程序类路径中的服务提供者将被忽略
        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);
        }
    
        /**
         * Returns a string describing this service.
         *
         * @return  A descriptive string
         */
        public String toString() {
            return "java.util.ServiceLoader[" + service.getName() + "]";
        }
    
    }

    ServiceLoader不是实例化以后,就去读取配置文件中的具体实现,并进行实例化。而是等到使用迭代器去遍历的时候,才会加载对应的配置文件去解析,调用hasNext方法的时候会去加载配置文件进行解析,调用next方法的时候进行实例化并缓存。

    所有的配置文件只会加载一次,服务提供者也只会被实例化一次,重新加载配置文件可使用reload方法。

     SPI缺点

    通过上面的解析,可以发现,我们使用SPI查找具体的实现的时候,需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要实现。这应该也是最大的缺点,需要把所有的实现都实例化了,即便我们不需要,也都给实例化了。

  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    block的是发送信号的线程,又不是处理槽函数的线程
  • 原文地址:https://www.cnblogs.com/warehouse/p/9335530.html
Copyright © 2011-2022 走看看