SPI
service provider interface,JDK内置提供的服务发现/加载机制;实现基于接口的编程模型; 实现定制化服务(可插拔加载不同的实现,通过jar内的配置文件(META-INF/services)实现策略调度)
SPI usage
- META-INF/services/文件名是接口的全名; 文件内容是实现类的全名; 行注释使用#
- 实现类在jar中时,Jar路径包要设置在classpath下;
只需要配置maven,就是指定实现 - 实现类需要有一个不带参数的构造函数, 用来
clazz.newInstance()创建实例对象 - 使用ServiceLoader.load() 加载实现类(默认使用当前线程的classLoader)
缺陷: 如果加载了全部的实现,不方便调用指定的某一个实现...
应用实例
common-logging
在早期的jar中配置文件META-INF/services/org.apache.commons.logging.LogFactory;实现日志门面接口,由各自的日志提供商自动实现,定制化实现
jdbc
不用再使用Class.forName()加载指定的驱动类,直接maven引入就可以;查看mysql-connector-java的jar的源文件
dubbo源码应用解析
dubbo-spi在jdk-spi的基础上进行加强,
- 实现动态根据key加载指定的实现,adaptive自适应
- 通过ios和aop实现依赖扩展和自动加载;
- Dubbo-spi在实现上引入了本地缓存和线程安全的考虑(loader是单例),牺牲内存但性能更高;
- 使用了代理设计模式实现了更加灵活的加载,例如Wrapper和依赖扩张的运行时加载...是dubbo模块的核心技术.
motan源码应用分析
self code
自己继承是实现ExtensionLoader();学习dubbo
jdk-spi的源码分析
// SPI的实现类,通过继承本能的支持iterator相关操作
// 只保留了关键的代码
public final class ServiceLoader<S> implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/";
// interface
private final Class<S> service;
private final ClassLoader loader;
// 访问控制,不懂可以忽略
private final AccessControlContext acc;
// 缓存已经实例化的实现
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// The current lazy-lookup iterator, 延迟迭代的内部类
// 控制延迟加载流程的核心内部类
private LazyIterator lookupIterator;
// 模式使用当前线程的classloader加载服务
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
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);
}
// 通过私有的内部类实现了 延迟的加载, 控制了延迟加载的流程
// 实现了加载的主要流程
// 增加了hasNextService(),nextService()控制主要的延迟加载的流程
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);
}
}
// 到此,jar内的所有配置文件全部加载完成
// 依次加载每个配置文件
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();
// 下一个实现类的权限定名
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);
}
// 卧槽...这是JVM异常
throw new Error(); // This cannot happen
}
public boolean hasNext() {
// 封装了权限的判断,底层是自己实现的hasNextService
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() {
// 封装了权限的判断,底层是自己实现的nextService
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 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();
}
// 加载这个文件到List(names), 返回加载的个数
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;
}
// 处理comment
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
// 配置失败直接Exception
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 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);
}
// Lazily loads the available providers of this loader's service.
// 使用了 providers,lookupIterator 返会自己的迭代器, 可以访问全部的实现类
public Iterator<S> iterator() {
return new Iterator<S>() {
// 已经加载的缓存
Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
public boolean hasNext() {
// 1.先判断缓存
if (knownProviders.hasNext())
return true;
// 2. 缓存没有则动态的加载
return lookupIterator.hasNext();
}
public S next() {
// 1. 先判断缓存
if (knownProviders.hasNext())
return knownProviders.next().getValue();
// 2,再动态判断加载
return lookupIterator.next();
}
// 当然不能删除,这里只提供迭代作用
public void remove() {
throw new UnsupportedOperationException();
}
};
}
// 使用jdk的classloader加载服务
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);
}
}
ServiceLoader不是在一开始实例化后就加载全部的实现,是通过内部类LazyIterator实现的延迟加载;
延迟加载的规则是,每次先加载一个文件,依次返回文件的一行(一个实现类)用于实例化服务类;一个文件遍历完成后再加载另一个文件,都是顺序加载
缺点: 会加载全部的服务实现,运行过程中不能指定运行某一个具体的服务实现类
public class JavaSPITest {
@Test
public void sayHello() throws Exception {
// 1. 会一次加载全部的服务实现类
ServiceLoader<Robot> robotServiceLoader = ServiceLoader.load(Robot.class);
System.out.println("Java SPI");
// 2. 没法选择指定的实现
// robotServiceLoader.iterator().forEachRemaining(Robot::sayHello);
//robotServiceLoader.forEach(Robot::sayHello);
//robotServiceLoader.reload();
// 3. 通过代码实现默认调用第一个实现,算是一种折中的解决方案
Robot first = robotServiceLoader.iterator().next();
first.sayHello();
}
}
模拟Dubbo实现简单的SPI,实现功能增强
实现了本地缓存 和 线程安全; 通过注解指定记载; 还可以继续加强
/**
* simple servier loader 对jdk-spi的服务增强
* 主要模仿dubbo-spi进行增强
* <p>
* 加强的规则
* 1. @SimpleSPI注解注解接口(指定默认的实现和实例的类型:单例/原型)
* 2. @SimpleSPIName注解指定实现类的Name,默认通过name加载指定的实现
* 3. 兼容jdk-spi
* 4. 1+2 的功能也可以通过配置文件实现
*/
// 每个loader是单例,需要处理线程安全的问题
public class SimpleServiceLoader<T> {
private static final Logger log = LoggerFactory.getLogger(SimpleServiceLoader.class);
// 支持扫描jdk-spi的目录
private static final String PREFIX = "META-INF/services/";
private static final String PREFIX_SIMPLE = PREFIX + "simple/";
// 使用类缓存,缓存所有的需要加载的接口,不需要重复加载
// 使用类的缓存应该是大写区分实例的缓存
private static final ConcurrentMap<Class<?>, SimpleServiceLoader<?>> CACHED_SERVICE_LOADER = new ConcurrentHashMap<Class<?>, SimpleServiceLoader<?>>();
// 使用实例缓存,type接口的所有实例缓存
private final ConcurrentMap<String, VolatileHolder<Object>> cachedInstances = new ConcurrentHashMap<String, VolatileHolder<Object>>();
// Type接口需要加载的全部实现类名缓存
private final VolatileHolder<Map<String, Class<?>>> cachedClasses = new VolatileHolder<Map<String, Class<?>>>();
// 接口,默认的实现
private final Class<T> type;
private final String cachedDefaultInstance;
public static <T> SimpleServiceLoader<T> getSimpleServiceLoader(Class<T> clz) {
if (clz == null) {
throw new IllegalArgumentException("service type == null");
}
if (!clz.isInterface()) {
throw new IllegalArgumentException("Service type (" + clz.getName() + ") is not interface");
}
// 兼容JDK-SPI 注解不是必须的
// if (!clz.isAnnotationPresent(SimpleSPI.class)) { }
SimpleServiceLoader<T> loader = (SimpleServiceLoader<T>) CACHED_SERVICE_LOADER.get(clz);
if (loader == null) {
CACHED_SERVICE_LOADER.putIfAbsent(clz, new SimpleServiceLoader<T>(clz));
loader = (SimpleServiceLoader<T>) CACHED_SERVICE_LOADER.get(clz);
}
return loader;
}
// 接口对应的loader实例
private SimpleServiceLoader(Class<T> type) {
this.type = type;
SimpleSPI simpleSPI = type.getAnnotation(SimpleSPI.class);
if (simpleSPI != null) {
cachedDefaultInstance = simpleSPI.value();
} else {
// 默认为空
cachedDefaultInstance = null;
}
}
private ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
public T getService(String name) {
// 判断默认输入
if (name == null || name.length() == 0) {
// 加载默认
return getDefaultService();
}
// 处理多线程并发的范式
// concurrenthashMap能解决多线程访问的效率问题,没法处理锁的问题
VolatileHolder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new VolatileHolder<Object>());
holder = cachedInstances.get(name);
}
// 处理线程安全问题
Object instance = holder.getValue();
if (instance == null) {
// 最小粒度加锁,不对当前loader实例加锁,因为loader加锁的粒度太大
// 对name加锁, 但是name是入参
// 对intance加锁, 但是instance是null
synchronized (holder) {
instance = holder.getValue();
if (instance == null) {
instance = createService(name);
holder.setValue(instance);
}
}
}
return (T) instance;
}
public T getDefaultService() {
if (cachedDefaultInstance == null || cachedDefaultInstance.length() == 0) {
return null;
}
return getService(cachedDefaultInstance);
}
// 创建实例Instance
private T createService(String name) {
// 所有的实现类
Class clz = getServiceClass(name);
if (clz == null) {
throw new IllegalArgumentException("service instance(" + name + ") not exists.");
}
// 直接创建实例,已经在实例层加锁
try {
Object instance = clz.newInstance();
return (T) instance;
} catch (Exception e) {
throw new IllegalArgumentException("create Service instance(" + name + ") error");
}
}
// 获取所有的实现的类名
// 接口调用一次加载接口的全部实现
private Class<?> getServiceClass(String name) {
Map<String, Class<?>> classMap = cachedClasses.getValue();
// 这个加锁
if (classMap == null) {
// 一次需要加载全部的实现,所以对整个缓存加锁,保证cachedClasses不是null, 且多线程可见
synchronized (cachedClasses) {
classMap = cachedClasses.getValue();
if (classMap == null) {
Map<String, Class<?>> tmpMap = loadServices();
cachedClasses.setValue(tmpMap);
}
}
}
return cachedClasses.getValue().get(name);
}
// 加载配置
private Map<String, Class<?>> loadServices() {
Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
loadDir(classMap, PREFIX);
loadDir(classMap, PREFIX_SIMPLE);
return classMap;
}
// 加载配置的目录
private void loadDir(Map<String, Class<?>> classMap, String path) {
String fileName = path + type.getName();
log.info("loadDir:{}", fileName);
try {
// 目录下所有的同名文件
Enumeration<URL> resources;
ClassLoader classloader = getClassLoader();
if (classloader != null) {
resources = classloader.getResources(fileName);
} else {
resources = ClassLoader.getSystemResources(fileName);
}
// 依次加载每个文件
if (resources != null) {
while (resources.hasMoreElements()) {
loadFile(classMap, classloader, resources.nextElement());
}
}
} catch (Exception e) {
log.error("loadDir:", e);
}
}
// 加载配置文件
private void loadFile(Map<String, Class<?>> classMap, ClassLoader classloader, URL url) {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
String line;
while ((line = br.readLine()) != null) {
// 注释简单的处理
if (line.indexOf("#") >= 0) {
continue;
}
Class<?> clz = Class.forName(line, true, classloader);
String name = clz.getSimpleName();
if (!type.isAssignableFrom(clz)) {
throw new IllegalArgumentException("class(" + clz.getName() + ") is not subclass of " + type);
}
// 实现类的name
if (!clz.isAnnotationPresent(SimpleSPIName.class)) {
SimpleSPIName spiName = clz.getAnnotation(SimpleSPIName.class);
name = spiName.name();
}
// 更新name格式
name = name.substring(0, 1).toLowerCase() + name.substring(1);
classMap.put(name, clz);
}
// 应该finally
br.close();
} catch (Exception e) {
log.error("loadFile", e);
}
}
// 测试
public static void main(String[] args) {
// 95a2dc313f6f49d166016c4ad492577e1d297ca68e10058f9135c2981f5fc760
SimpleServiceLoader<Robot> loader = SimpleServiceLoader.getSimpleServiceLoader(Robot.class);
// 默认调用
Robot defaultService = loader.getDefaultService();
defaultService.sayHello();
// 指定调用
Robot optimusPrime = loader.getService("optimusPrime");
optimusPrime.sayHello();
}
}