参考文章:Dubbo的SPI机制分析
首先来看看 Java SPI 的机制
Java SPI 起初是提供给厂商做插件开发用的,例如数据库驱动java.sql.Driver,市面上各种各样的数据库,不同的数据库底层协议都不一样,为了方便开发者调用数据库而不用关心它们之间的差异,因此必须提供一个统一的接口来规范和约束这些数据库。有了统一的接口,数据库厂商就可以按照规范去开发自己的数据库驱动了。
厂商开发好数据库驱动了,应用如何使用呢?该使用哪个驱动呢?以 MySQL 为例,早期手写 JDBC 时,开发者需要手动注册驱动,现在已经不需要了,就是利用了 SPI 机制。
Java SPI 使用了策略模式,一个接口多种实现,开发者面向接口编程,具体的实现并不在程序中直接硬编码,而是通过外部文件进行配置。
Java SPI 约定了一个规范,使用步骤如下:
- 编写一个接口。
- 编写具体实现类。
- 在 ClassPath 下的META-INF/services,目录创建以接口全限定名命名的文件,文件内容为实现类的全限定名,多个实现用换行符分割。
- 通过 ServiceLoader 类获取具体实现。
接口:
实现类:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
简单实例
package org.sun.spi.services;
/**
* 这是java 的 spi, 没有@SPI注解, 类似 mySQL
*/
public interface Say {
void say();
}
实现类1
package org.sun.spi.impl;
import org.sun.spi.services.Say;
public class SayImpl implements Say {
@Override
public void say() {
System.out.println("nihao .....");
}
}
实现类2
package org.sun.spi.impl;
import org.sun.spi.services.Say;
public class SayWrapper implements Say {
@Override
public void say() {
System.out.println("hello SayWrapper。。。。");
}
}
META-INF/services 文件
测试:
public class Main {
public static void main(String[] args) {
ServiceLoader<Say> serviceLoader = ServiceLoader.load(Say.class);
serviceLoader.forEach(say -> say.say());
}
结果:
nihao .....
hello SayWrapper。。。。
Dubbo SPI机制
Dubbo SPI 定义了一套自己的规范,同时对 Java SPI 存在的问题进行了改进,优点如下:
- 扩展类按需加载,节约资源。
- SPI 文件采用 Key=Value 形式,可以根据扩展名灵活获取实现类。
- 扩展类对象做了缓存,避免重复创建。
- 扩展类加载失败有详细日志,方便排查。 支持 AOP 和 IOC。
Dubbo SPI 使用规范:
- 编写接口,接口必须加@SPI 注解,代表它是一个可扩展的接口。
- 编写实现类。
- 在 ClassPath 下的META-INF/dubbo,目录创建以接口全限定名命名的文件,文件内容为 Key=Value 格式,Key 是扩展点的名称,Value 是扩展点实现类的全限定名。
- 通过 ExtensionLoader 类获取扩展点实现。
Dubbo 默认会扫描META-INF/services
、META-INF/dubbo
、META-INF/dubbo/internal
三个目录下的配置,第一个是为了兼容 Java SPI,第三个是 Dubbo 内部使用的扩展点。
测试用例
// TODO
源码分析:
ExtensionLoader.getExtensionLoader
getExtension()
public T getExtension(String name, boolean wrap) {
checkDestroyed();
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
// 如果 name 为true,则返回一个默认的扩展点
if ("true".equals(name)) {
return getDefaultExtension();
}
String cacheKey = name;
if (!wrap) {
cacheKey += "_origin";
}
// 创建或者返回一个holder对象, 用于缓存扩展类的实例
final Holder<Object> holder = getOrCreateHolder(cacheKey);
Object instance = holder.get();
// 如果缓存不存在则创建一个实例
if (instance == null) {
// 同步设置,懒汉模式
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
return (T) instance;
PS : 有意思的类,学习
/**
* 持有一个泛型
* Helper Class for hold a value.
*/
public class Holder<T> {
// 保证线程可见性
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
上述代码就是先查缓存,如果未命中,则创建一个扩展对象,createExtension() 应该就是去指定的路径下查找name 对应的扩展点实现,并且实例化之后返回。
@SuppressWarnings("unchecked")
private T createExtension(String name, boolean wrap) {
// 根据 name 返回扩展类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
// 从缓存中查找该类是否已经初始化
// ConcurrentMap<Class<?>, Object> extensionInstances = new ConcurrentHashMap<>(64);
T instance = (T) extensionInstances.get(clazz);
if (instance == null) {
// 如果没有,则重新创建一个实例并加入缓存, 反射机制
extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
instance = (T) extensionInstances.get(clazz);
// 初始化前的相关操作
instance = postProcessBeforeInitialization(instance, name);
// 依赖注入
injectExtension(instance);
instance = postProcessAfterInitialization(instance, name);
}
if (wrap) {
// 通过 Wrapper 进行包装
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
boolean match = (wrapper == null) ||
((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
!ArrayUtils.contains(wrapper.mismatches(), name));
if (match) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
instance = postProcessAfterInitialization(instance, name);
}
}
}
}
// Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
继续跟踪getExtensionClasses
- 从缓存中获取已经被加载的扩展类
- 如果未命中缓存, 则调用loadExtensionClasses 加载扩展类
private Map<String, Class<?>> getExtensionClasses() {
// Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
loadExtensionClasses()
/**
* synchronized in getExtensionClasses
*/
private Map<String, Class<?>> loadExtensionClasses() {
checkDestroyed();
// 获得当前type扩展接口的默认扩展对象, 并且缓存
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
// 加载指定文件目录下的配置文件
loadDirectory(extensionClasses, strategy, type.getName());
// compatible with old ExtensionFactory
if (this.type == ExtensionInjector.class) {
loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
}
}
return extensionClasses;
}
(精彩后续,请看下回分解)