SPI,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。Java中SPI机制主要思想是将装配的控制权移到程序之外,其核心思想就是解耦。 这里面有三个核心概念:
publicfinalclassServiceLoader<S> implementsIterable<S>{ // The class or interface representing the service being loaded privatefinal Class<S> service;
// The class of the service type privatefinal String serviceName;
// The module layer used to locate providers; null when locating // providers using a class loader privatefinal ModuleLayer layer;
// The class loader used to locate, load, and instantiate providers; // null when locating provider using a module layer privatefinal ClassLoader loader;
// The access control context taken when the ServiceLoader is created privatefinal AccessControlContext acc;
// The lazy-lookup iterator for iterator operations private Iterator<Provider<S>> lookupIterator1; privatefinal List<S> instantiatedProviders = new ArrayList<>();
// The lazy-lookup iterator for stream operations private Iterator<Provider<S>> lookupIterator2; privatefinal List<Provider<S>> loadedProviders = new ArrayList<>(); privateboolean loadedAllProviders; // true when all providers loaded
// Incremented when reload is called privateint reloadCount;
if (VM.isBooted()) { checkCaller(caller, svc); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } } else {
// if we get here then it means that ServiceLoader is being used // before the VM initialization has completed. At this point then // only code in the java.base should be executing. Module callerModule = caller.getModule(); Module base = Object.class.getModule(); Module svcModule = svc.getModule(); if (callerModule != base || svcModule != base) { fail(svc, "not accessible to " + callerModule + " during VM init"); }
// restricted to boot loader during startup cl = null; }
// create lookup iterator if needed if (lookupIterator1 == null) { lookupIterator1 = newLookupIterator(); }
returnnew Iterator<S>() {
// record reload count finalint expectedReloadCount = ServiceLoader.this.reloadCount;
// index into the cached providers list int index;
/** * Throws ConcurrentModificationException if the list of cached * providers has been cleared by reload. */ privatevoidcheckReloadCount(){ if (ServiceLoader.this.reloadCount != expectedReloadCount) thrownew ConcurrentModificationException(); }
@Override public S next(){ checkReloadCount(); S next; if (index < instantiatedProviders.size()) { next = instantiatedProviders.get(index); } else { next = lookupIterator1.next().get(); instantiatedProviders.add(next); } index++; return next; }
Set<String> providerNames = new HashSet<>(); // to avoid duplicates //存放所有实现类的全限定名 Enumeration<URL> configs; //当前类对象的迭代器 Iterator<String> pending; //当前迭代的实现类 Provider<T> nextProvider; ServiceConfigurationError nextError;
LazyClassPathLookupIterator() { }
/** * Parse a single line from the given configuration file, adding the * name on the line to set of names if not already seen. */ privateintparseLine(URL u, BufferedReader r, int lc, Set<String> names) throws IOException { 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); int start = Character.charCount(cp); for (int i = start; 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 (providerNames.add(ln)) { names.add(ln); } } return lc + 1; }
/** * Parse the content of the given URL as a provider-configuration file. */ private Iterator<String> parse(URL u){ Set<String> names = new LinkedHashSet<>(); // preserve insertion order try { URLConnection uc = u.openConnection(); uc.setUseCaches(false); try (InputStream in = uc.getInputStream(); BufferedReader r = new BufferedReader(new InputStreamReader(in, UTF_8.INSTANCE))) { int lc = 1; while ((lc = parseLine(u, r, lc, names)) >= 0); } } catch (IOException x) { fail(service, "Error accessing configuration file", x); } return names.iterator(); }
/** * Loads and returns the next provider class. */ private Class<?> nextProviderClass() { if (configs == null) { try { //META-INF/services/ 加上接口的全限定类名,就是文件服务类的文件 //META-INF/services/com.viewscenes.netsupervisor.spi.SPIService String fullName = PREFIX + service.getName(); if (loader == null) { //将文件路径转成URL对象 configs = ClassLoader.getSystemResources(fullName); } elseif (loader == ClassLoaders.platformClassLoader()) { // The platform classloader doesn't have a class path, // but the boot loader might. if (BootLoader.hasClassPath()) { configs = BootLoader.findResources(fullName); } else { configs = Collections.emptyEnumeration(); } } else { //将文件路径转成URL对象 configs = loader.getResources(fullName); } } catch (IOException x) { fail(service, "Error locating configuration files", x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { returnnull; } //解析URL文件对象,读取内容,最后返回 pending = parse(configs.nextElement()); } String cn = pending.next(); try { //加载类对象 return Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); returnnull; } }
/** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */ static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }