SPI 机制
根本概述
SPI
全称Service Provider Interface
,是一种服务发现机制。通过提供接口、预约义的加载器 (Loader
) 以及约定俗称的配置(个别在META-INF
目录下),能够实现动静加载服务实现类。
类图
通过类图能够剖析出,ServiceLoader
实现了 Iterable
接口,提供了迭代的性能。
而 ServiceLoader
将迭代的实现委托给 LazyIterator
。
LazyIterator
提供了延时迭代的能力,当有须要的时候,才去加载。
在 Skywalking
模块中的应用
接口定义
org.apache.skywalking.oap.server.library.module.ModuleDefine
package org.apache.skywalking.oap.server.library.module;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.Properties;
import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A module definition.
*/
public abstract class ModuleDefine implements ModuleProviderHolder {private static final Logger LOGGER = LoggerFactory.getLogger(ModuleDefine.class);
private ModuleProvider loadedProvider = null;
private final String name;
public ModuleDefine(String name) {this.name = name;}
/**
* @return the module name
*
*/
public final String name() {return name;}
/**
* @return the {@link Service} provided by this module.
*/
public abstract Class[] services();
/**
* Run the prepare stage for the module, including finding all potential providers, and asking them to prepare.
*
* @param moduleManager of this module
* @param configuration of this module
* @throws ProviderNotFoundException when even don't find a single one providers.
*/
void prepare(ModuleManager moduleManager, ApplicationConfiguration.ModuleConfiguration configuration,
ServiceLoader<ModuleProvider> moduleProviderLoader) throws ProviderNotFoundException, ServiceNotProvidedException, ModuleConfigException, ModuleStartException {// etc...}
// etc...
@Override
public final ModuleProvider provider() throws DuplicateProviderException, ProviderNotFoundException {if (loadedProvider == null) {throw new ProviderNotFoundException("There is no module provider in" + this.name() + "module!");
}
return loadedProvider;
}
}
接口实现
org.apache.skywalking.oap.server.library.module.BaseModuleA
package org.apache.skywalking.oap.server.library.module;
public class BaseModuleA extends ModuleDefine {public BaseModuleA() {super("BaseA");
}
// 须要提供服务的接口
@Override
public Class<? extends Service>[] services() {return new Class[] {
ServiceABusiness1.class,
ServiceABusiness2.class
};
}
public interface ServiceABusiness1 extends Service {void print();
}
public interface ServiceABusiness2 extends Service {}}
META-INF
定义
META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine
org.apache.skywalking.oap.server.library.module.BaseModuleA
应用形式
org.apache.skywalking.oap.server.library.module.ModuleManager#init
/**
* Init the given modules
*/
public void init(ApplicationConfiguration applicationConfiguration) /* etc... */ {
// SPI 机制加载
ServiceLoaderModuleDefine> moduleServiceLoader = ServiceLoader.load(ModuleDefine.class);
// 迭代器获取
for (ModuleDefine module : moduleServiceLoader) {
// do something
// etc...
}
// etc...
}
源码解析
package java.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class ServiceLoader<S> implements Iterable<S> {
// 目录前缀
private static final String PREFIX = "META-INF/services/";
// 须要被加载对象的 Class 对象
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;
// 重载
public void reload() {
// 革除加载对象缓存
providers.clear();
// 重置懒加载迭代器
lookupIterator = new LazyIterator(service, loader);
}
// 不容许间接创立 ServiceLoader 对象,只能通过 loadXXX 获取 ServiceLoader 对象
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('\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;
}
// 解析配置文件,返回实现类名列表
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 class LazyIterator implements Iterator<S> {
// 须要被加载对象的 Class 对象
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;
}
// 是否有下一个 Service
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;
}
// 获取下一个 Service
private S nextService() {if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
// 类名 -> 类的 Class 对象
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();
}
};
}
// 通过类的 Class 对象及类加载,获取 ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {return new ServiceLoader<>(service, loader);
}
// 通过类的 Class 对象,获取 ServiceLoader
public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
// 通过类的 Class 对象和扩大类加载器,获取 ServiceLoader
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);
}
public String toString() {return "java.util.ServiceLoader[" + service.getName() + "]";
}
}
PS:JDK
提供的 SPI
机制,必须要应用迭代器遍历获取须要的实现,而 Dubbo SPI 能够通过 #getExtension
获取指定实现类。
总结
通过源码剖析,能够理解到 Skywalking
没有定义本人的 SPI
机制,但深刻浏览 Skywalking
的应用场景后,发现用 JDK
提供的 SPI
机制也没什么问题。
集体认为,任何技术都应该依据场景选取,适宜的才是最好的,如果没有那么简单的须要,没必要像 dubbo
一样,定义本人的 SPI
机制。
参考文档
- Java Service Provider Interface
-
Dubbo SPI
分享并记录所学所见