编程语言
首页 > 编程语言> > Java 中的 SPI 机制

Java 中的 SPI 机制

作者:互联网

Java 中的 SPI 机制

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件(服务接口),自动加载文件里所定义的类(服务接口的具体实现类)。

当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。这样就能很好的找到服务接口的实现类,而不需要再代码里制定

jdk提供服务实现查找的一个工具类:java.util.ServiceLoader

这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。我们先通过一个很简单的例子来看下它是怎么用的。

SPI 案例

先定义一个接口 SpiService

public interface SpiService {
    void execute();
}

再定义两个实现类 SpiImplOne、SpiImplTwo

public class SpiImplOne implements SpiService {
    @Override
    public void execute() {
        System.out.println("SpiImplOne.execute()");
    }
}
public class SpiImplTwo implements SpiService {
    @Override
    public void execute() {
        System.out.println("SpiImplTwo.execute()");
    }
}

然后在 ClassPath 路径下的 META-INF/services 文件夹下添加一个文件。文件名字是接口(SpiService)的全限定类名,内容是实现类的全限定类名,多个实现类用换行符分隔。

com.tinady.spi.SpiImplOne
com.tinady.spi.SpiImplTwo

测试

public class Test {

    public static void main(String[] args) {
        ServiceLoader<SpiService> loader = ServiceLoader.load(SpiService.class);
        Iterator<SpiService> it = loader.iterator();
        while (iterator.hasNext()) {
            SpiService spiService = iterator.next();
            spiService.execute();
        }

        System.out.println("=======================");
        Iterator<SpiService> providers = Service.providers(SpiService.class);
        while (providers.hasNext()) {
            SpiService spiService = providers.next();
            spiService.execute();
        }
    }
}

上面代码是通过ServiceLoader.load 或者 Service.providers 两种方法拿到实现类的实例。其中 ServiceLoader 类位于 java.util.ServiceLoader 包下;Service 类位于 sun.misc.Service 包下

源码分析

类介绍

查看 Java API,看看里面怎么说的

A simple service-provider loading facility.

Providers are located and instantiated lazily, that is, on demand. A service loader maintains a cache of the providers that have been loaded so far. Each invocation of the {@link #iterator iterator} method returns an iterator that first yields all of the elements of the cache, in instantiation order, and then lazily locates and instantiates any remaining providers, adding each one to the cache in turn. The cache can be cleared via the {@link #reload reload} method.

大致意思:ServiceLoader 是一个简单的服务提供商加载工具…提供商的位置很懒,即按需实例化。 服务加载器维护到目前为止已加载的提供者的缓存。 每次iterator() 方法的调用都会返回一个迭代器,该迭代器首先按实例化顺序生成高速缓存的所有元素,然后懒惰地定位和实例化任何剩余的提供程序,依次将每个提供程序添加到高速缓存中。 可以通过reload() 方法清除缓存…(具体文档说明请自行查阅)

可以简单地理解:ServiceLoader 也像 ClassLoader 一样,能装载类文件,但是也有区别---- ServiceLoader 装载的是一系列有某种共同特征的实现类;而ClassLoader 是个万能类加载器

ServiceLoader 类的结构

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 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 class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;
        
        ...
    }
}

load() 方法

load() 方法初始化了 ServiceLoader 类的一些属性,重要的是实例化了内部类属性-----LazyIterator lookupIterator(懒加载)

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);
}

parse() 方法

解析文件夹下的配置文件,按照行读取。默认使用的是 utf-8 文件编码

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();
}

iterator() 方法

这里使用了一个匿名内部类来返回一个 Iterator。

在解析配置文件过程中,通过 LinkedHashMap<String,S> 数据结构的 providers,将已经发现了的接口实现类进行了缓存,并对外提供的 iterator() 方法,方便外部遍历访问。

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 类中完成。当我们调用 iterator.hasNext() 和 iterator.next() 方法的时候,实际上调用的都是 LazyIterator 的相应方法。

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();
        }
    };
}

所以,我们重点关注 lookupIterator.hasNext() 方法,它最终会调用到 hasNextService

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);
    }
}

在 hasNextService() 方法中调用了 parse() 方法,并将解析后的值赋值给 pending 属性

private boolean hasNextService() {
    // 第二次调用 hasNextService() 方法时,配置文件已经解析完毕,nextName 不为 null,所以,可以直接返回
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            // META-INF/services/ 加上接口的全限定类名,就是文件服务类的文件名
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                // 将文件路径转成URL对象
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        // //解析URL文件对象,读取内容,最后返回
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

创建实例

当然,调用 next()方法的时候,实际调用到的是,lookupIterator.nextService()。它通过反射的方式,创建实现类的实例并将它进行缓存(存入到 providers 属性中),然后返回。

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);
    }
}

在 nextService() 方法中进行实例化

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 {
        // 通过newInstance实例化
        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
}

标签:return,iterator,service,ServiceLoader,SPI,Java,机制,null,public
来源: https://blog.csdn.net/Lucky_Boy_Luck/article/details/110264905