概念
SPI(service provider interface),是JDK内置的一种服务提供发现机制。是一种动态替换发现机制,比如有个接口,想在运行时动态地给它添件实现,只需要添加一个实现。
然后在META-INF/services目录创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类(全限定名)。
源码实现
-
创建ServiceLoader
ServiceLoader.load(Driver.class); 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(); }
-
服务实现获取
-
初始化LazyIterator
public Iterator<S> iterator() { return new Iterator<S>() { //ServiceLoader.LazyIterator的封装对象 Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator(); public boolean hasNext() { //ServiceLoader已经从classPath读取过从构造参数传递进来的指定接口的子类,则会将全限定类名和对应实例缓存起来 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(); } }; }
-
调用layIterator的forEachRemaining方法
default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } private class LazyIterator implements Iterator<S> { private boolean hasNextService() { .... } private S nextService() { .... } 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(); } }
- 调用hasNext ->hasNextService
- next -> nextService
-
判断是否还有服务提供
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(); //nextName是在hasNextService中赋值的 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 }
-
hasNextService() -> getResource()
public Enumeration<URL> getResources(String name) throws IOException { @SuppressWarnings("unchecked") Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2]; if (parent != null) { //ExtensionClassLoader tmp[0] = parent.getResources(name); } else { //BootstrapClassLoader tmp[0] = getBootstrapResources(name); } //加载当前类加载器 tmp[1] = findResources(name); return new CompoundEnumeration<>(tmp); }
这里是递归调用,需注意类加载器的层级关系 Application ClassLoader -> Extension ClassLoader -> BootStrap ClassLoader
-
findResource
public Enumeration<URL> findResources(final String name) throws IOException { final Enumeration<URL> e = ucp.findResources(name, true); return new Enumeration<URL>() { private URL url = null; private boolean next() { if (url != null) { return true; } do { URL u = AccessController.doPrivileged( new PrivilegedAction<URL>() { public URL run() { if (!e.hasMoreElements()) return null; return e.nextElement(); } }, acc); if (u == null) break; url = ucp.checkURL(u); } while (url == null); return url != null; } public URL nextElement() { if (!next()) { throw new NoSuchElementException(); } URL u = url; url = null; return u; } public boolean hasMoreElements() { return next(); } }; }
从UrlClassPath中获取Enumeration
-
UrlClassPath.findResource()
public Enumeration<URL> findResources(final String name, final boolean check) { return new Enumeration<URL>() { // 懒加载的方式 private int index = 0; private int[] cache = getLookupCache(name); private URL url = null; private boolean next() { if (url != null) { return true; } else { Loader loader; //遍历UrlClassPath的所有loader,直到为空,并用index记录位置,因为再次调用时要获取 while ((loader = getNextLoader(cache, index++)) != null) { //用获取到的loader加载指定资源 url = loader.findResource(name, check); if (url != null) { return true; } } return false; } } }; }
-
getLoader()
private synchronized Loader getLoader(int index) { if (closed) { return null; } while (loaders.size() < index + 1) { URL url; synchronized (urls) { if (urls.empty()) { return null; } else { url = urls.pop(); } } String urlNoFragString = URLUtil.urlNoFragString(url); if (lmap.containsKey(urlNoFragString)) { continue; } Loader loader; try { loader = getLoader(url); URL[] urls = loader.getClassPath(); if (urls != null) { push(urls); } } catch (IOException e) { continue; } catch (SecurityException se) { if (DEBUG) { System.err.println("Failed to access " + url + ", " + se ); } continue; } // Finally, add the Loader to the search path. validateLookupCache(loaders.size(), urlNoFragString); loaders.add(loader); lmap.put(urlNoFragString, loader); } if (DEBUG_LOOKUP_CACHE) { System.out.println("NOCACHE: Loading from : " + index ); } return loaders.get(index); }
UrlClassPath构造函数会传入待加载的url,并将其存入stack,如果处理了该url就将其抛出,所有url只会被处理一次。
//代办为空 或 代办中没有元素开始遍历下一个类加载器获取代办 while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, configs.nextElement()); } nextName = pending.next();
-
hasNextService返回True,forEachRemaining()方法中的hasNext也为True,继续遍历