dubbo的SPI加载机制分析

dubbo的SPI加载机制分析

如何加载对应类

在dubbo中通过ExtensionLoader来实现对应的类,来看看具体的代码实现。
首先来看看它的加载类ExtensionLoader类的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}

ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

由于EXTENSION_LOADERS.get(type)为空;,那么接下来会执行EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));那么会执行它的构造函数构造函数,加载Class为com.alibaba.dubbo.common.extension.ExtensionFactory的ExtensionLoader。
其代码表现形式为:

1
2
3
4
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

当调用的Type不为ExtensionFactory.class时,会调用getAdaptiveExtension这个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}

return (T) instance;
}

这段代码重点关注createAdaptiveExtension()这个方法:

1
2
3
4
5
6
7
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}

其中核心的步骤在于getAdaptiveExtensionClass这个方法,下面有个getExtensionClasses方法,顾名思义,就是拿到所有的扩展类。

1
2
3
4
5
6
7
8
9
10
11
12
13
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}

这里有一个loadExtensionClasse方法:它主要是加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}

Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

其中比较核心的是loadFile这个方法,该方法从指定文件夹中读取对应的文件并将类实例化或者缓存到HashMap中。分别从三个文件夹下获取:

  1. META-INF/services/
  2. META-INF/dubbo/
  3. META-INF/dubbo/internal

其文件夹下面保存文件的形式分别是这种:
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
而且有意思的是这个类必须继承自文件的名字,因为文件的名字即为类的名字。把相关的类加载或者扩展加载进来。带有Adaptive注解的是默认的即将加载的类。没有带的将缓存到cachedClasses中。接着就是调用构造函数生成一个新的实例。

获取类的实例

如果有多个实例,想要通过name获取对应的实例,可以通过
ExtensionLoader.getExtensionLoader(SimpleExt.class).getExtension(“impl1”)
这个方法获取。

总结

大概分析完了dubbo的类加载机制,其实它本质就是通过配置加上注解来实现的。我们也可以自己动手写一个简单的类加载实例。