JAVA SPI

不想你离开。 提交于 2019-12-22 00:24:28

SPI发现机制


Springmvc的无web.xml启动方式也是用的SPI自动发现机制处理的,都是通过在配置文件中加入实现类全限定名。
标准接口:

package spi;

public interface Fruit {
    void name();
}

插拔式实现类1:

package spi;

public class Apple implements Fruit {

    @Override
    public void name() {
        System.out.println("apple");
    }
}

插拔式实现类2:

package spi;

public class Orange implements Fruit {
    @Override
    public void name() {
        System.out.println("orange");
    }
}

程序调用方式:

package spi;

import java.util.Iterator;
import java.util.ServiceLoader;

public class SPIMain {
    public static void main(String[] args) {
        ServiceLoader<Fruit> fruits = ServiceLoader.load(Fruit.class);
        Iterator<Fruit> iterator = fruits.iterator();
        while (iterator.hasNext()) {
            Fruit fruit = iterator.next();
            fruit.name();
        }
    }
}

具体读取的配置文件:
META-INF/services/spi.Fruit

spi.Apple
spi.Orange

META-INF/services/是默认前缀,spi.Fruit是需要拓展的标准接口全限定名,内容为具体的实现类全限定名。SPI的实现类必须携带一个不带参数的构造方法。

ServiceLoader类分析:

//实现Iterable接口提供迭代器
public final class ServiceLoader<S>
    implements Iterable<S>
{
	//定义读取的配置文件目录
    private static final String PREFIX = "META-INF/services/";

    //定义可以拓展的类或者接口
    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;
	......    
	
	//主要内部类
    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);
                }
            }
            //看到并不是一次性全部读取加载的,只有当调用此方法发现没有保存的集合中没有剩余类时才加载
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                //返回解析出实现类全限定名的迭代器,先判断属性providers是否包含这个类
                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 {
            	//初始化配置的实现类,false表示不执行类加载过程中的初始化阶段
                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 {
            	//反射生成实现类对象,需要有一个无参构造函数,并且转换为service类型
                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();
        }

    }

	//解析配置实现类的关键方法
    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;
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!