【注意】最后更新于 December 2, 2018,文中内容可能已过时,请谨慎使用。
一、 Java SPI(Service Provider Interface)
1.SPI思想
具有可插拔性,在软件系统的设计中,可插拔是一个重要特性。它意味着给系统添加新功能的时候(或者将原来功能的实
现替换成新的实现而保持接口不变),不改变系统已有的功能。
我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。
面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,
就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,
这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。
2.JAVA中SPI的约定
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。
该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置
文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码
里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader.

二、Duboo中spi实现
1.dubbo中基于SPI思想的实现
Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来。
Dubbo改进了JDK标准的SPI的以下问题:
- JDK标准的SPI会ServiceLoader迭代实例化扩展类,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。
比如:JDK标准的ScriptEngine,通过getName();获取脚本类型的名称,但如果RubyScriptEngine因为所依赖的jruby.jar不存在,
导致RubyScriptEngine类加载失败,这个失败原因被吃掉了,和ruby对应不起来,当用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因。
- 扩展类不能进行setter注入,不支持AOP和IOC支持。
2.Doubbo中SPI的约定
当服务的提供者,提供了服务接口的一种实现之后,在jar包的### META-INF/dubbo/internal/,META-INF/dubbo/
和META-INF/services/三个目录中里同时创建一个以服务接口命名的文件。com.alibaba.dubbo.common.extension.ExtensionLoader
提供服务实现查找的一个工具类

3.说一说注解
- @SPI在接口类,配置名称,当作默认实现类的名称,是必须;
- @Adaptive配置在实现类或者接口方法上;
- 配置实现类,不用动态生成适配器类,且只能配置一个;
- 配置接口方法上,作用于动态生产适配器时,动态实现方法;未配置不用实现接口方法,直接throw异常;
- @Activate类注解,用于分组和key进行过滤;获取Filter集合;
三、ExtensionLoader源码分析
- SPI是怎么加载接口实现类的class?
- why设计适配器类?
- 是怎么实现DI呢?
- 用什么方式按分组和key获取相应的实现类集合?
从几个问题ExtensionLoader源码分析;
1.loadExtensionClasses方法加载接口实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// synchronized in getExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
//设置cachedDefaultName 默认名称,就是注解SPI的value值
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
//不能配置多个name;
if (names.length > 1) {
...
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
//从指定文件加载相应值
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 从META-INF/dubbo/internal/,META-INF/dubbo/和META-INF/services 三个文件中获取type接口的文件
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
|
a.loadFile 从文件中加载内容
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
91
92
93
94
95
96
97
98
99
|
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
//获取类加载类,根据类加载获取文件URL
ClassLoader classLoader = findClassLoader();//ExtensionLoader.class.getClassLoader
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
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) {
// 根据key=value 解析内容,valeu相应实现类
try {
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);
//判断type和clazz是否相同或是另一个类的子类或接口。
if (!type.isAssignableFrom(clazz)) {
...
}
//类配置@Adaptive注解,切只有一个,赋值变量cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
...包含多个throw异常
}
} 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();
//在文件中key为空,从注解Extension获取name,现在已用弃用
if (name == null || name.length() == 0) {
...
}
//根据','配置多个name
String[] names = NAME_SEPARATOR.split(name)
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
//缓存注解Activate
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
//缓存名字
cachedNames.put(clazz, n);
}
//扩展类加载到extensionClasses集合中
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
...相同的名字thow异常
}
}
}
}
}
}
} catch (Throwable t) {
...把异常信息添加到缓存中
}
}
}
} finally {
reader.close();
}
}...
}
}
}...
}
|
loadFile逻辑缓存步骤:
-
(1).查找Class类上是否@Adaptive注解,查找Class类中,只有一个@Adaptive注解,有多个throw异常;缓存cachedAdaptiveClass;
假如成立,不执行2,3,4步骤;
-
(2).查找Class类中构造方法是否该类型Class,缓存到Set集合cachedWrapperClasses中;假如成立,不执行3,4步骤;
-
(3).查找Class类上是否@Activate注解,把Activate对象缓存到Map集合cachedActivates;
-
(4).缓存Map集合cachedNames中(class—>name);
b.getExtensionClasses 获取实现type接口的实现类集合,且只处理loadExtensionClasses一次,加载结果缓存;
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;
}
|
2.看看获取适配器类的流程;从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
|
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
//cachedAdaptiveInstance是Holder对象,且只能创建一个
Object instance = cachedAdaptiveInstance.get();
//cachedAdaptiveInstance缓存获取
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) { //双重验证
try {
//调用创建适配器类
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);//缓存cachedAdaptiveInstance
} catch (Throwable t) {
createAdaptiveInstanceError = t;
...
}
}
}
}...
}
return (T) instance;
}
|
a.createAdaptiveExtension 创建实例对象
实现DI注入
1
2
3
4
5
6
|
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
}...
}
|
b.getAdaptiveExtensionClass
1
2
3
4
5
6
7
8
9
10
|
private Class<?> getAdaptiveExtensionClass() {
//处理加载实现类
getExtensionClasses();
//cachedAdaptiveClass 就是注解@Adaptive的Class
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//动态生成适配器类
return cachedAdaptiveClass = createAdaptiveExtensionClass();//缓存到cachedAdaptiveClass
}
|
3.如何动态生成适配器类,createAdaptiveExtensionClass入口
1
2
3
4
5
6
7
8
9
|
private Class<?> createAdaptiveExtensionClass() {
//生成动态代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
//获取对象是AdaptiveCompiler适配类,默认获取@SPI注解的名称javassist,
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//最终交给JavassistCompiler编译成class,底层用javassist
return compiler.compile(code, classLoader);
}
|
a.createAdaptiveExtensionClassCode生成动态代码;
这个方法代码量大,不美观;按几个步骤去分析;
- 按type+$Adaptive组装成类名
- 遍历实现接口方法,
- 是否有@Adaptive注解,否则:throw的UnsupportedOperationException
- 从方法参数中查找到URL的位置,URL作用用于获取适配器类中调用相应实现类名称
- URL用什么方式获取name呢?
- @Adaptive注解配置value,value是数组,未配置,就获取type名称,有大写字母变成小写字母前面加’.';
- 获取分三个种;
- value包含protocol,获取方式url.getProtocol();
- 参数包含Invocation类型,获取方式url.getMethodParameter(methodName,
- 不满足上面情况,获取方式url.getParameter
- value配置多个时,从大到小遍历;为空情况往后获取;则参数为Invocation情况,只取第一个情况;
- extension = ExtensionLoader.getExtensionLoader(type.class).getExtension(extName)
- extension拼接成调用相应方法
4.DI注入实现,injectExtension方法入口
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 injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
//setter
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;
}
|
a.objectFactory对象创建注入的对象;就是ExtensionFactory
- 自己实现适配类AdaptiveExtensionFactory
- SpiExtensionFactory和SpringExtensionFactory实现类,
- 获取对象时,遍历上面类,获取对象;
5.getExtensionLoader 获取ExtensionLoader实例,每个接口类型对应一个ExtensionLoader;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
...
if (!type.isInterface()) {
...异常
}
if (!withExtensionAnnotation(type)) { //type.isAnnotationPresent(SPI.class) SPI注解
... 异常
}
//先从EXTENSION_LOADERS缓存中获取ExtensionLoader对象,没用创建ExtensionLoader,在EXTENSION_LOADERS放置缓存中
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;
}
|
6.属性
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
|
public class ExtensionLoader<T> {
private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
//dubbo的SPI扫描文件地址
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
//缓存ExtensionLoader实例
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
//缓存未被包装过对象
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
// ==============================
private final Class<?> type;
//DI注入工厂类
private final ExtensionFactory objectFactory;
//缓存(class-name)
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
//缓存(name-class)
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
//缓存注解@Activate
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//缓存包装过实例对象
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
//缓存Adaptive实例对象
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//缓存被@Adaptive注解的Class
private final Class<?> cachedAdaptiveClass = null;
//接口@SIP中value值
private String cachedDefaultName;
//创建适配类异常信息
private volatile Throwable createAdaptiveInstanceError;
//缓存包装类
private Set<Class<?>> cachedWrapperClasses;
//操作loadFile方法是,异常保存
private final Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
}
|
7.构造
1
2
3
4
5
6
|
//私有构造方法
private ExtensionLoader(Class<?> type) {
this.type = type;
//当type是ExtensionFactory,objectFactory为null;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
|
8.getExtension 按名称获取对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);//从缓存获取对象
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
|
a.createExtension 创建扩展类对象
- 从cachedClasses缓存获取按name获取相应实现class
- DI注入操作
- 包装类操作
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) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
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);//setter注入
Set<Class<?>> wrapperClasses = cachedWrapperClasses;//包装类集合缓存
//层层包装这个类
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
}...
}
|
9.getActivateExtension 按名称获取集合对象
- values是否包含’-defualt’
- 否,按组过滤,在按Url的参数是否包含values的key和value
- 是不处理默认逻辑
- 按values获取相应的name
- 包含’-‘就是删除操作
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
|
/**
* Get activate extensions.
*
* @param url url
* @param values extension point names 过滤cachedActivates缓存中名称
* @param group group 比配@Activate中group
* @return extension list which are activated
* @see com.alibaba.dubbo.common.extension.Activate
*/
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)) {//比较- defualt
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {//比配@Activate中group属性
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {//url参数集合中是否包含@Activate注解中value数组至少一个值
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR); //按@Activate注解进行排序
}
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.size() > 0) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (usrs.size() > 0) {
exts.addAll(usrs);
}
return exts;
}
|
(1).isMatchGroup 比配@Activate中group属性
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private boolean isMatchGroup(String group, String[] groups) {
if (group == null || group.length() == 0) {
return true;
}
if (groups != null && groups.length > 0) {
for (String g : groups) {
if (group.equals(g)) {
return true;
}
}
}
return false;
}
|
(2).isActive url参数集合中是否包含@Activate注解中value数组至少一个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
private boolean isActive(Activate activate, URL url) {
String[] keys = activate.value();
if (keys == null || keys.length == 0) {
return true;
}
for (String key : keys) {
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ConfigUtils.isNotEmpty(v)) {
return true;
}
}
}
return false;
}
|
10.总结
- 运用适配类动态获取相应的实现类;更好扩展;
- 扩展DI注入以及注入可以集成spring的容器的bean;以及包装类实现;