1. dubbo spi介绍
Dubbo 按照 SPI 配置文件的用途,将spi配置文件目录分成了三类目录:
- META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI
- META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件
- META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件。
然后Dubbo 将 SPI 配置文件改成了 KV 格式,例如:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
中 key 被称为扩展名(也就是 ExtensionName),当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现。例如接口org.apache.dubbo.rpc.Protocol
有几个实类,这里指定扩展名为 dubbo,Dubbo SPI 就知道我们要使用:org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
这个扩展实现类,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类。使用 KV 格式的 SPI 配置文件的另一个好处是:让我们更容易定位到问题。假设我们使用的一个扩展实现类所在的 jar 包没有引入到项目中,那么 Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载。这些更加准确的异常信息降低了排查问题的难度,提高了排查问题的效率。
2. @SPI注解
dubbo 中某个接口被 @SPI注解修饰时,就表示该接口是扩展接口,如:
@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol {
}
相应配置文件为:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
@SPI 注解的 value 值指定了默认的扩展名称,例如,在通过 Dubbo SPI 加载 Protocol 接口实现类时,如果SPI配置文件目录下org.apache.dubbo.rpc.Protocol文件中没有明确指定扩展名,则默认会将接口org.apache.dubbo.rpc.Protocol上 @SPI 注解的 value 值作为扩展名.以上为例即加载 dubbo 这个扩展名对应的 org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol 这个扩展实现类。
dubbo spi应用示例(配置文件和前面的一模一样,省略):
ExtensionLoader<Protocol> extensionLoader = ApplicationModel.defaultModel()
.getDefaultModule().getExtensionLoader(Protocol.class);
Protocol protocol = extensionLoader.getExtension("dubbo");
ExtensionLoader的核心字段:
public class ExtensionLoader<T> {
/**
* 缓存了扩展实现类与其实例对象的映射关系。在前文示例中,Key 为 Class,Value为接口实例对象
* 如果: key: class org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
* value为DubboProtocol实例对象
*
*/
private final ConcurrentMap<Class<?>, Object> extensionInstances = new ConcurrentHashMap<>(64);
/**
* 当前ExtensionLoader实例负责加载的扩展spi接口class
*/
private final Class<?> type;
/**
* 缓存了该ExtensionLoader实例对象加载的spi接口的所有的扩展实现类与扩展名之间的映射关系:
* 如: key value
* {Class@1826} "class org.apache.dubbo.rpc.protocol.rest.RestProtocol" -> rest
* {Class@1835} "class org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol" -> injvm
* {Class@1828} "class org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol" -> registry
* {Class@1832} "class org.apache.dubbo.rpc.support.MockProtocol" -> mock
* {Class@1827} "class org.apache.dubbo.registry.integration.RegistryProtocol" -> service-discovery-registry
* {Class@1829} "class org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol" -> dubbo
*/
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
/**
* 缓存了该 ExtensionLoader 加载的扩展名与扩展实现类之间的映射关系。cachedNames 集合的反向关系缓存
*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
/**
* 缓存了该ExtensionLoader加载的扩展名与扩展实现对象Holder之间的映射关系:
* 这里如: key value
* dubbo Holder实例对象(ProtocolSerializationWrapper<ProtocolFilterWrapper<ProtocolListenerWrapper<DubboProtocol>>>)
*/
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
private volatile Class<?> cachedAdaptiveClass = null;
/**
* spi接口上@SPI注解的value值,也就是默认扩展名
*/
private String cachedDefaultName;
/**
* LoadingStrategy接口有三个实现类都通过JDK SPI的方式加载,在dubbo-common项目的META_INF/services目录下创建了该接口的spi配置文件,
* 文件中列出了实现的接口类,有如下三个:
* org.apache.dubbo.common.extension.DubboInternalLoadingStrategy 加载META-INF/dubbo/internal/目录下的spi配置文件
* org.apache.dubbo.common.extension.DubboLoadingStrategy 加载META-INF/dubbo/目录下的spi配置文件
* org.apache.dubbo.common.extension.ServicesLoadingStrategy 兼容jdk,加载META-INF/services/目录下的配置文件
*/
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
}
ExtensionDirector#getExtensionLoader方法分析:
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
checkDestroyed();
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 1. find in local cache
ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
ExtensionScope scope = extensionScopeMap.get(type);
if (scope == null) {
SPI annotation = type.getAnnotation(SPI.class);
scope = annotation.scope();
extensionScopeMap.put(type, scope);
}
if (loader == null && scope == ExtensionScope.SELF) {
// create an instance in self scope
loader = createExtensionLoader0(type);
}
// 2. find in parent
if (loader == null) {
if (this.parent != null) {
loader = this.parent.getExtensionLoader(type);
}
}
// 3. create it
if (loader == null) {
loader = createExtensionLoader(type);
}
return loader;
}
private <T> ExtensionLoader<T> createExtensionLoader(Class<T> type) {
ExtensionLoader<T> loader = null;
if (isScopeMatched(type)) {
// if scope is matched, just create it
loader = createExtensionLoader0(type);
}
return loader;
}
// 创建ExtensionLoader
private <T> ExtensionLoader<T> createExtensionLoader0(Class<T> type) {
checkDestroyed();
ExtensionLoader<T> loader;
// 实例化ExtensionLoader,并放入map暂存
extensionLoadersMap.putIfAbsent(type, new ExtensionLoader<T>(type, this, scopeModel));
loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);
return loader;
}
// 构造方法
ExtensionLoader(Class<?> type, ExtensionDirector extensionDirector, ScopeModel scopeModel) {
this.type = type;
this.extensionDirector = extensionDirector;
this.extensionPostProcessors = extensionDirector.getExtensionPostProcessors();
initInstantiationStrategy();
this.injector = (type == ExtensionInjector.class ? null : extensionDirector.getExtensionLoader(ExtensionInjector.class)
.getAdaptiveExtension());
this.activateComparator = new ActivateComparator(extensionDirector);
this.scopeModel = scopeModel;
}
得到接口对应的 ExtensionLoader 对象之后会调用其 getExtension() 方法,根据传入的扩展名称返回相应的实例对象, ExtensionLoader#getExtension():
public T getExtension(String name) {
// 核心方法
T extension = getExtension(name, true);
if (extension == null) {
throw new IllegalArgumentException("Not find extension: " + name);
}
return extension;
}
public T getExtension(String name, boolean wrap) {
checkDestroyed();
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
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;
}
private T createExtension(String name, boolean wrap) {
// 获取 cachedClasses 缓存,根据扩展名从cachedClasses缓存中获取扩展实现类Class
// 如果 cachedClasses 未初始化,则会扫描前面介绍的三个 SPI 目录获取查找相应的 SPI 配置文件,然后加载其中的扩展实现类,
// 最后将扩展名和扩展实现类的映射关系记录到 cachedClasses 缓存中。这部分逻辑在 loadExtensionClasses() 和 loadDirectory() 方法中。
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
// 从extensionInstances里查找
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) {
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);
}
}
3. JDK SPI 、dubbo spi 、spring spi的比较
JDK SPI: 每个扩展点单独一个文件;只能通过ServiceLoader的迭代器遍历获取扩展实现类,同时也会加载所有实现类,这样就会把用不到的类也会进行实例化,浪费一定资源;是java自带实现,不需要引入第三方依赖可直接使用.
dubbo spi:每个扩展点一个配置文件,文件名为接口的全限定名.支持别名的概念,可以通过别名获取某个具体扩展点的实现.配置文件的内容为key=value形式,key是别名,value是实现类的全限定名.
spring spi: spring指定配置文件为classpath下的META-INF/spring.factories, 所有的扩展点配置都放到这个文件中.配置文件内容为key=value的形式,key为接口的全限定名,value为实现类的全限定名,可以为多个.