Dubbo SPI拓展机制

SPI拓展机制(IOC、AOP、自适应拓展)

Dubbo 官方文档 拓展点加载 介绍了Dubbo的SPI拓展机制,总结起来如下:

  1. JDK SPI的加强版:
    • 只有使用的时候才会实例化拓展点,而摒弃JDK SPI一次性实例化所有拓展点
    • 增加了对IOC、AOP的支持
  2. 拓展的方法:在扩展类的 jar 包内,放置扩展点配置文件 META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。即可被Dubbo自动加载。
  3. 扩展点的特性:
    • 扩展点自动装配(IOC):加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,在会自动注入依赖的扩展点;
    • 扩展点自动包装:AOP的一种实现方式,为拓展点自动添加一层包装,将某些可拓展的共有逻辑放在包装类(Wrapper)中。调用时,会先调用包装类的逻辑再调用拓展点的内容;
    • 扩展点自适应:在拓展点方法执行的时候,根据调用的参数(URL)决定调用的是哪个拓展点实现,而不会在初始化时就直接指定是哪个拓展点;
    • 扩展点自动激活:对于集合类拓展点,可以同时加载多个实现。自动激活可以简化配置,且可以编排它们的顺序。

ExtensionLoader是实现其SPI拓展机制的关键类,Dubbo会为每种类型拓展点都创建有一个ExtensionLoader实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//首先从缓存里获取
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;
}
//私有构造函数
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

其最主要的属性如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//所处理的拓展点类型(接口类型)
Class<?> type;
//拓展点工厂,用于在拓展点注入属性时获取属性的实例(从SPI中获取或者从Spring的上下文中获取)
ExtensionFactory objectFactory;
//拓展点类和名称的映射
ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
// 名称和类的映射,类可能有多个名称
Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
// 名称与激活拓展点注解的映射。
Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//名称与拓展点实例的映射,一般一个拓展点只会有一个实例
ConcurrentMap<String, Holder<Object>> cachedInstances = new
ConcurrentHashMap<String, Holder<Object>>();
// 自适应拓展点实例
Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//自实例拓展点类型
Class<?> cachedAdaptiveClass = null;
String cachedDefaultName;
//包装类集合 AOP
Set<Class<?>> cachedWrapperClasses;

对外暴露的最主要的方法如下:

1
2
3
4
5
6
//获取自适应拓展
public T getAdaptiveExtension();
//根据name获取特定的拓展实例
public T getExtension(String name);
// 根据条件获取当前扩展可自动激活的实现
public List<T> getActivateExtension(URL url, String[] values, String group);

Dubbo初始化时,获取拓展点时一般是调用getActivateExtension获取自适应拓展点,然后在运行时调用方法时,根据实时的参数URL确定拓展点实例的name,调用getExtension(name)获取实例。下面我们依次看这三个实现方法:

getAdaptiveExtension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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) {
...
}
}
}
} else {
...
}
}
return (T) instance;
}
private T createAdaptiveExtension() {
try {
//先实例化自适应对象实例,后注入属性
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}...
}
private Class<?> getAdaptiveExtensionClass() {
//加载所有type类型类
getExtensionClasses();
//若有自适应类信息,则直接返回
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//没有时,自动生成自适应类(xxx$Adaptive)
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

上文分析中有三个比较关键的方法getExtensionClassescreateAdaptiveExtensionClassinjectExtension。下面,我们重点来分析它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//最终会调用loadExtensionClasses来加载所有type类型的Class
getExtensionClass(String name)
- getExtensionClasses().get(name)
- loadExtensionClasses()

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls == null) {
return;
}
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
String line = null;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//加载类
Class<?> clazz = Class.forName(line, true, classLoader);
//如果是自适应类,则设置
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
}...
} else {
try {
//如果有以type为入参的构造函数,则说明类是包装类,加入包装类缓存中
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//不是包装类时
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//如果带有自动激活注解,则说明是自动激活类,缓存到...
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
...
}
}
}
}
}
}...
}
} // end of while read lines
} // end of while urls
}

当没有自适应类时,需要自动拼装自适应类,并编译。

1
2
3
4
5
6
7
8
9
private Class<?> createAdaptiveExtensionClass() {
//生成自适应类代码
//自适应方法的内容,会根据type方法上@Adaptive的属性生成
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//编译
return compiler.compile(code, classLoader);
}

创建类后,需要注入属性,injectExtension方法负责其工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private T injectExtension(T instance) {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 获取自适应的属性
Object object = objectFactory.getExtension(pt, property);
// 注入属性
if (object != null) {
method.invoke(instance, object);
}
} ...
}
}
}
return instance;
}

getExtension

getAdapativeExtension执行中,已经加载了所有的type拓展类,初始化了type对应的ExtensionLoader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private T createExtension(String name) {
//loadFile中加载的所有拓展类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
//创建对象
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
//注入属性
injectExtension(instance);
//如果有包装类,进行包装
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
}

getActivateExtension

根据条件获取当前扩展可自动激活的实现。比如FilterInvokerListenerExporterListener
getActivateExtension有多个重载函数,最主要的实现在下面这个函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}

讲完它的原理,来看几个源码中的例子更好的理解下:

您的支持是我创作源源不断的动力