dubbo的SPI机制类似与Java的SPI,Java的SPI会一次性的实例化所有扩展点的实现,有点显得浪费资源。

  • dubbo的扩展机制可以方便的获取某一个想要的扩展实现,每个实现都有自己的name,可以通过name找到具体的实现。
  • 每个扩展点都有一个@Adaptive实例,用来注入到依赖这个扩展点的某些类中,运行时通过url参数去动态判断最终选择哪个Extension实例用。
  • dubbo的SPI扩展机制增加了对扩展点自动装配(类似IOC)和自动包装(类似AOP)的支持。
  • 标注了@Activate的扩展点实现类,可以通过getActivateExtension方法获取,可以根据条件加载一些自动激活的扩展点。

扩展点自动装配(IOC)功能:

1
2
3
4
接口A,实现类A1,A2
接口B,实现类B1,B2

实现类A1含有setB()方法,就会自动的注入一个B的实现类,但是不是注入B1和B2,而是注入一个动态生成的B的实现类B$Adpative,该实现能够根据参数的不同,自动选择B1或者B2来完成相应功能。

扩展点自动包装(AOP)功能:

其实是对扩展点采用装饰器模式进行增强。

1
2
3
4
5
接口A还有另外一个实现者:AWrapper1,此实现者有如下构造器:
private A a;
AWrapper1(A a){
this.a = a;
}

当我们再获取接口A的实现类的时候,就已经被AWrapper1包装过了,我们得到的就是包装过得类。

定义

@SPI注解

被此注解标记的接口,就是可扩展的接口。

@Adaptive注解

@Adaptive有两种注解方式:一种是注解在类上,一种是注解在方法上。

注解在类上

注解在类上,而且是注解在实现类上,目前dubbo只有AdaptiveCompiler和AdaptiveExtensionFactory类上标注了此注解,这是些特殊的类,ExtensionLoader需要依赖他们工作,所以得使用此方式。

注解在方法上

注解在接口的方法上,除了上面两个类之外,所有的都是注解在方法上。ExtensionLoader根据接口定义动态的生成适配器代码,并实例化这个生成的动态类。被Adaptive注解的方法会生成具体的方法实现。没有注解的方法生成的实现都是抛不支持的操作异常UnsupportedOperationException。被注解的方法在生成的动态类中,会根据url里的参数信息,来决定实际调用哪个扩展。

比如说这段代码:

1
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

当上面代码执行的时候,我们其实还不知道要真正使用的Protocol是什么,可能是具体的实现DubboProtocol,也可能是其他的具体实现的Protocol,那么这时候refprotocol到底是什么呢?refprotocol其实是在调用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
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");

com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.refer(arg0, arg1);
}

public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");

if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();

String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.export(arg0);
}

public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}

public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
}

可以看到被@Adaptive注解的方法都生成了具体的实现,并且实现逻辑都相同。而没有被注解的方法直接抛出不支持操作的异常。

当我们使用refprotocol调用方法的时候,其实是调用生成的类Protocol$Adpative中的方法,这里面的方法根据url中的参数配置来找到具体的实现类,找具体实现类的方式还是通过dubbo的扩展机制。比如url中可能会有protocol=dubbo,此时就可以根据这个dubbo来确定我们要找的类是DubboProtocol。可以查看下生成的代码中getExtension(extName)这里是根据具体的名字去查找实现类。

@Activate注解

此注解需要注解在类上或者方法上,并注明被激活的条件,以及所有的被激活实现类中的排序信息。

ExtensionLoader

ExtensionLoader是dubbo的SPI机制的查找服务实现的工具类,类似与Java的ServiceLoader,可做类比。dubbo约定扩展点配置文件放在classpath下的/META-INF/dubbo,/META-INF/dubbo/internal,/META-INF/services目录下,配置文件名为接口的全限定名,配置文件内容为配置名=扩展实现类的全限定名

重点解析下ExtensionLoader这个类。dubbo的扩展点使用单一实例去加载,缓存在ExtensionLoader中。每一个ExtensionLoader实例仅负责加载特定SPI扩展的实现,想要获得某个扩展的实现,首先要获得该扩展对应的ExtensionLoader实例。

以Protocol为例进行分析扩展点的加载:

1
2
//这样使用,加载Protocol扩展点
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

第一步,getExtensionLoader(Protocol.class),根据要加载的接口Protocol,创建出一个ExtensionLoader实例,加载完的实例会被缓存起来:

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
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//扩展点类型不能为空
if (type == null)
throw new IllegalArgumentException();
//扩展点类型只能是接口类型的
if(!type.isInterface()) {
throw new IllegalArgumentException();
}
//没有添加@SPI注解,只有注解了@SPI的才会解析
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException();
}
//先从缓存中获取指定类型的ExtensionLoader
//EXTENSION_LOADERS是一个ConcurrentHashMap,缓存了所有已经加载的ExtensionLoader的实例
//比如这里加载Protocol.class,就以Protocol.class作为key,以新创建的ExtensionLoader作为value
//每一个要加载的扩展点只会对应一个ExtensionLoader实例,也就是只会存在一个Protocol.class在缓存中
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
//缓存中不存在
if (loader == null) {
//创建一个新的ExtensionLoader实例,放到缓存中去
//对于每一个扩展,dubbo中只有一个对应的ExtensionLoader实例
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}

上面代码返回一个ExtensionLoader,getExtensionLoader(Protocol.class)这一步没有进行任何的加载工作,只是获得了一个ExtensionLoader的实例。加载是在调用getAdaptiveExtension()方法中进行的:

1
2
3
4
5
getAdaptiveExtension()-->
createAdaptiveExtension()-->
getAdaptiveExtensionClass()-->
getExtensionClasses()-->
loadExtensionClasses()

先看下getAdaptiveExtension()方法,用来获取一个扩展的自适应实现类,最后返回的自适应实现类是一个类名为Protocol$Adaptive,并且这个类实现了Protocol接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public T getAdaptiveExtension() {
//先从实例缓存中查找实例对象
//private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//在当前的ExtensionLoader中保存着一个Holder实例,用来缓存自适应实现类的实例
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {//缓存中不存在
if(createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
//获取锁之后再检查一次缓存中是不是已经存在
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
//缓存中没有,就创建新的AdaptiveExtension实例
instance = createAdaptiveExtension();
//新实例加入缓存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {createAdaptiveInstanceError = t; }
}
}
}
}

return (T) instance;
}

接着看下createAdaptiveExtension()方法,用来创建自适应扩展类的实例:

1
2
3
4
5
6
7
8
private T createAdaptiveExtension() {
try {
//先通过getAdaptiveExtensionClass获取AdaptiveExtensionClass
//然后获取其实例
//最后进行注入处理
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {}
}

接着查看getAdaptiveExtensionClass()方法,用来获取一个自适应扩展的Class,这个Class将会在下一步被实例化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private Class<?> getAdaptiveExtensionClass() {
//加载当前Extension的所有实现(这里举例是Protocol,只会加载Protocol的所有实现类),如果有@Adaptive类型的实现类,会赋值给cachedAdaptiveClass
//目前只有AdaptiveExtensionFactory和AdaptiveCompiler两个实现类是被注解了@Adaptive
getExtensionClasses();
//加载完所有的实现之后,发现有cachedAdaptiveClass不为空
//也就是说当前获取的自适应实现类是AdaptiveExtensionFactory或者是AdaptiveCompiler,就直接返回,这两个类是特殊用处的,不用代码生成,而是现成的代码
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//没有找到Adaptive类型的实现,动态创建一个
//比如Protocol的实现类,没有任何一个是用@Adaptive来直接的,只有Protocol接口的方法是有注解的
//这时候就需要来动态的生成了,也就是生成Protocol$Adaptive
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

先看下getExtensionClasses()这个方法,加载所有的扩展类的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private Map<String, Class<?>> getExtensionClasses() {
//从缓存中获取,cachedClasses也是一个Holder,Holder这里持有的是一个Map,key是扩展点实现名,value是扩展点实现类
//这里会存放当前扩展点类型的所有的扩展点的实现类
//这里以Protocol为例,就是会存放Protocol的所有实现类
//比如key为dubbo,value为com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
//cachedClasses扩展点实现名称对应的实现类
Map<String, Class<?>> classes = cachedClasses.get();
//如果为null,说明没有被加载过,就会进行加载,而且加载就只会进行这一次
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//如果没有加载过Extension的实现,进行扫描加载,完成后缓存起来
//每个扩展点,其实现的加载只会这执行一次
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}

看下loadExtensionClasses()方法,这个方法中加载扩展点的实现类:

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
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if(defaultAnnotation != null) {
//当前Extension的默认实现名字
//比如说Protocol接口,注解是@SPI("dubbo")
//这里dubbo就是默认的值
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();
}
//默认的名字保存起来
if(names.length == 1) cachedDefaultName = names[0];
}
}

//下面就开始从配置文件中加载扩展实现类
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//从META-INF/dubbo/internal目录下加载
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
//从META-INF/dubbo/目录下加载
loadFile(extensionClasses, DUBBO_DIRECTORY);
//从META-INF/services/下加载
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}

从各个位置的配置文件中加载实现类,对于Protocol来说加载的文件是以com.alibaba.dubbo.rpc.Protocol为名称的文件,文件的内容是(有好几个同名的配置文件,这里直接把内容全部写在了一起):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol

dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol

hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol

com.alibaba.dubbo.rpc.protocol.http.HttpProtocol

injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol

memcached=memcom.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol

redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol

rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol

thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol

com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol

看下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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
//配置文件的名称
//这里type是扩展类,比如com.alibaba.dubbo.rpc.Protocol类
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
//获取类加载器
ClassLoader classLoader = findClassLoader();
//获取对应配置文件名的所有的文件
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) {
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是Protocol接口
//clazz就是Protocol的各个实现类
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException();
}
//如果实现类是@Adaptive类型的,会赋值给cachedAdaptiveClass,这个用来存放被@Adaptive注解的实现类
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException();
}
} else {//不是@Adaptice类型的类,就是没有注解@Adaptive的实现类
try {//判断是否是wrapper类型
//如果得到的实现类的构造方法中的参数是扩展点类型的,就是一个Wrapper类
//比如ProtocolFilterWrapper,实现了Protocol类,
//而它的构造方法是这样public ProtocolFilterWrapper(Protocol protocol)
//就说明这个类是一个包装类
clazz.getConstructor(type);
//cachedWrapperClasses用来存放当前扩展点实现类中的包装类
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//没有上面提到的构造器,则说明不是wrapper类型
//获取无参构造
clazz.getConstructor();
//没有名字,就是配置文件中没有xxx=xxxx.com.xxx这种
if (name == null || name.length() == 0) {
//去找@Extension注解中配置的值
name = findAnnotationName(clazz);
//如果还没找到名字,从类名中获取
if (name == null || name.length() == 0) {
//比如clazz是DubboProtocol,type是Protocol
//这里得到的name就是dubbo
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(");
}
}
}
//有可能配置了多个名字
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//是否是Active类型的类
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
//第一个名字作为键,放进cachedActivates这个map中缓存
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
//放入Extension实现类与名称映射的缓存中去,每个class只对应第一个名称有效
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
//放入到extensionClasses缓存中去,多个name可能对应一份extensionClasses
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException();
}
}
}
}
}
}
} catch (Throwable t) { }
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) { }
} // end of while urls
}
} catch (Throwable t) { }
}

到这里加载当前Extension的所有实现就已经完成了,继续返回getAdaptiveExtensionClass中,在调用完getExtensionClasses()之后,会首先检查是不是已经有@Adaptive注解的类被解析并加入到缓存中了,如果有就直接返回,这里的cachedAdaptiveClass中现在只能是AdaptiveExtensionFactory或者AdaptiveCompiler中的一个,如果没有,说明是一个普通扩展点,就动态创建一个,比如会创建一个Protocol$Adaptive

看下createAdaptiveExtensionClass()这个方法,用来动态的创建自适应扩展类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private Class<?> createAdaptiveExtensionClass() {
//组装自适应扩展点类的代码
String code = createAdaptiveExtensionClassCode();
//获取到应用的类加载器
ClassLoader classLoader = findClassLoader();
//获取编译器
//dubbo默认使用javassist
//这里还是使用扩展点机制来找具体的Compiler的实现
//现在就知道cachedAdaptiveClass是啥意思了,如果没有AdaptiveExtensionFactory和AdaptiveCompiler这两个类,这里又要去走加载流程然后来生成扩展点类的代码,不就死循环了么。
//这里解析Compiler的实现类的时候,会在getAdaptiveExtensionClass中直接返回
//可以查看下AdaptiveCompiler这个类,如果我们没有指定,默认使用javassist
//这里Compiler是JavassistCompiler实例
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
//将代码转换成Class
return compiler.compile(code, classLoader);
}

接着看下createAdaptiveExtensionClassCode()方法,用来组装自适应扩展类的代码(拼写源码,代码比较长不在列出),这里列出生成的Protocol$Adaptive

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
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");

com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.refer(arg0, arg1);
}

public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");

if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();

String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.export(arg0);
}

public void destroy() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}

public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
}

其他具体的扩展点的生成也类似。在生成完代码之后,是找到ClassLoader,然后获取到Compiler的自适应实现,这里得到的就是AdaptiveCompiler,最后调用compiler.compile(code, classLoader);来编译上面生成的类并返回,先进入AdaptiveCompiler的compile方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
//得到一个ExtensionLoader
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
//默认的Compiler名字
String name = DEFAULT_COMPILER; // copy reference
//有指定了Compiler名字,就使用指定的名字来找到Compiler实现类
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {//没有指定Compiler名字,就查找默认的Compiler的实现类
compiler = loader.getDefaultExtension();
}
//调用具体的实现类来进行编译
return compiler.compile(code, classLoader);
}

先看下根据具体的名字来获取扩展的实现类loader.getExtension(name);,loader是ExtensionLoader<Compiler>类型的。这里就是比Java的SPI要方便的地方,Java的SPI只能通过遍历所有的实现类来查找,而dubbo能够指定一个名字查找。代码如下:

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
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如果name指定为true,则获取默认实现
if ("true".equals(name)) {
//默认实现查找在下面解析
return getDefaultExtension();
}
//先从缓存获取Holder,cachedInstance是一个ConcurrentHashMap,键是扩展的name,值是一个持有name对应的实现类实例的Holder。
Holder<Object> holder = cachedInstances.get(name);
//如果当前name对应的Holder不存在,就创建一个,添加进map中
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
//从Holder中获取保存的实例
Object instance = holder.get();
//不存在,就需要根据这个name找到实现类,实例化一个
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//缓存不存在,创建实例
instance = createExtension(name);
//加入缓存
holder.set(instance);
}
}
}
//存在,就直接返回
return (T) instance;
}

创建扩展实例,createExtension(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
private T createExtension(String name) {
//getExtensionClasses加载当前Extension的所有实现
//上面已经解析过,返回的是一个Map,键是name,值是name对应的Class
//根据name查找对应的Class
Class<?> clazz = getExtensionClasses().get(name);
//如果这时候class还不存在,说明在所有的配置文件中都没找到定义,抛异常
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);
//Wrapper的包装
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) { }
}

有关属性注入和Wrapper的包装,下面再讲。到这里Compiler就能获得到一个指定name的具体实现类的实例了,然后就是调用实例的compile()方法对生成的代码进行编译。

如果在AdaptiveCompiler中没有找到指定的名字,就会找默认的扩展实现loader.getDefaultExtension();

1
2
3
4
5
6
7
8
9
10
11
12
public T getDefaultExtension() {
//首先还是先去加载所有的扩展实现
//加载的时候会设置默认的名字cachedDefaultName,这个名字是在@SPI中指定的,比如Compiler就指定了@SPI("javassist"),所以这里是javassist
getExtensionClasses();
if(null == cachedDefaultName || cachedDefaultName.length() == 0
|| "true".equals(cachedDefaultName)) {
return null;
}
//根据javassist这个名字去查找扩展实现
//具体的过程上面已经解析过了
return getExtension(cachedDefaultName);
}

关于javassist编译Class的过程暂先不说明。我们接着流程看:

1
2
3
4
5
6
7
8
 private T createAdaptiveExtension() {
try {
//先通过getAdaptiveExtensionClass获取AdaptiveExtensionClass(在上面这一步已经解析了,获得到了一个自适应实现类的Class)
//然后获取其实例,newInstance进行实例
//最后进行注入处理injectExtension
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) { }
}

接下来就是有关扩展点的注入的问题了,injectExtension,关于注入的解释查看最上面扩展点自动装配(IOC)的说明,injectExtension方法:

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
//这里的实例是Xxxx$Adaptive
private T injectExtension(T instance) {
try {
//关于objectFactory的来路,先看下面的解析
//这里的objectFactory是AdaptiveExtensionFactory
if (objectFactory != null) {
//遍历扩展实现类实例的方法
for (Method method : instance.getClass().getMethods()) {
//只处理set方法
//set开头,只有一个参数,public
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
//set方法参数类型
Class<?> pt = method.getParameterTypes()[0];
try {
//setter方法对应的属性名
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
//根据类型和名称信息从ExtensionFactory中获取
//比如在某个扩展实现类中会有setProtocol(Protocol protocol)这样的set方法
//这里pt就是Protocol,property就是protocol
//AdaptiveExtensionFactory就会根据这两个参数去查找对应的扩展实现类
//这里就会返回Protocol$Adaptive
Object object = objectFactory.getExtension(pt, property);
if (object != null) {//说明set方法的参数是扩展点类型,进行注入
//为set方法注入一个自适应的实现类
method.invoke(instance, object);
}
} catch (Exception e) { }
}
}
}
} catch (Exception e) {}
return instance;
}

有关AdaptiveExtensionFactory中获取Extension的过程,会首先在实例化的时候得到ExtensionFactory的具体实现类,然后遍历每个ExtensionFactory的实现类,分别在每个ExtensionFactory的实现类中获取Extension。

这里使用SpiExtensionFactory的获取扩展的方法为例,getExtension,也是先判断给定类是否是注解了@SPI的接口,然后根据类去获取ExtensionLoader,在使用得到的ExtensionLoader去加载自适应扩展。

objectFactory的来路,在ExtensionLoader中有个私有构造器:

1
2
3
4
5
6
//当我们调用getExtensionLoader这个静态方法的时候,会触发ExtensionLoader类的实例化,会先初始化静态变量和静态块,然后是构造代码块,最后是构造器的初始化
private ExtensionLoader(Class<?> type) {
this.type = type;
//这里会获得一个AdaptiveExtensionFactory
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

到此为止createAdaptiveExtension方法解析完成,接着返回上层getAdaptiveExtension()方法中,发现创建完自适应扩展实例之后,就会加入到cachedAdaptiveInstance缓存起来,然后就会返回给调用的地方一个Xxx$Adaptive实例。

走到这里,下面的代码就解析完了:

1
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

我们的到了一个Protocol$Adaptive实例,接着就是调用了,比如说我们要调用refprotocol.refer(Class<T> type, URL url))方法,由于这里refprotocol是一个Protocol$Adaptive实例,所以就先调用这个实例的refer方法,这里的实例的代码在最上面:

1
2
3
4
5
6
7
8
9
10
11
12
13
//这里为了好看,代码做了精简,包名都去掉了
public Invoker refer(Class arg0, URL arg1) throws Class {
if (arg1 == null) throw new IllegalArgumentException();

URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );

if(extName == null) throw new IllegalStateException();

Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);

return extension.refer(arg0, arg1);
}

可以看到这里首先根据url中的参数获取扩展名字,如果url中没有就使用默认的扩展名,然后根据扩展名去获取具体的实现。关于getExtension(String 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
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如果name指定为true,则获取默认实现
if ("true".equals(name)) {
//默认实现查找在下面解析
return getDefaultExtension();
}
//先从缓存获取Holder,cachedInstance是一个ConcurrentHashMap,键是扩展的name,值是一个持有name对应的实现类实例的Holder。
Holder<Object> holder = cachedInstances.get(name);
//如果当前name对应的Holder不存在,就创建一个,添加进map中
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
//从Holder中获取保存的实例
Object instance = holder.get();
//不存在,就需要根据这个name找到实现类,实例化一个
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//缓存不存在,创建实例
instance = createExtension(name);
//加入缓存
holder.set(instance);
}
}
}
//存在,就直接返回
return (T) instance;
}

创建扩展实例,createExtension(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
private T createExtension(String name) {
//getExtensionClasses加载当前Extension的所有实现
//上面已经解析过,返回的是一个Map,键是name,值是name对应的Class
//根据name查找对应的Class
//比如name是dubbo,Class就是com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
Class<?> clazz = getExtensionClasses().get(name);
//如果这时候class还不存在,说明在所有的配置文件中都没找到定义,抛异常
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);
}
//这里实例就是具体实现的实例了比如是DubboProtocol的实例
//属性注入,在上面已经解析过了,根据实例中的setXxx方法进行注入
injectExtension(instance);
//Wrapper的包装
//cachedWrapperClasses存放着所有的Wrapper类
//cachedWrapperClasses是在加载扩展实现类的时候放进去的
//Wrapper类的说明在最上面扩展点自动包装(AOP)
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
//比如在包装之前的instance是DubboProtocol实例
//先使用构造器来实例化当前的包装类
//包装类中就已经包含了我们的DubboProtocol实例
//然后对包装类进行injectExtension注入,注入过程在上面
//最后返回的Instance就是包装类的实例。
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
//这里返回的是经过所有的包装类包装之后的实例
return instance;
} catch (Throwable t) { }
}

获取的Extension是经过层层包装的扩展实现,然后就是调用经过包装的refer方法了,这就到了具体的实现中的方法了。

到此为止调用refprotocol.refer(Class<T> type, URL url))方法的过程也解析完了。

关于getActivateExtension方法的解析,等下再添加。

Dubbo中SPI扩展机制解析的更多相关文章

  1. 使用Dubbo的SPI扩展机制实现自定义LoadBalance——方法一 修改Dubbo源代码

    一. 拉取源码 到Dubbo官网 https://github.com/apache/incubator-dubbo/tree/2.5.x 下载源码,解压. 二. 导入IDEA 选择解压后的源码目录, ...

  2. 使用Dubbo的SPI扩展机制实现自定义LoadBalance——方法二 不改源码添加META-INF/dubbo元数据

    一.官网提供的方法 参考官网 http://dubbo.apache.org/zh-cn/docs/dev/impls/load-balance.html 二.方法总结 在工程中创建类并实现LoadB ...

  3. Dubbo中@Service工作过程解析

    Dubbo中@Service工作过程解析 Spring中的BeanPostProcessor 首先我们应当了解到在spring体系中BeanPostProcessor是什么.加载流程 它是什么 Bea ...

  4. DubboSPI机制二之Dubbo中SPI初体验

    Dubbo高级之一SPI机制之JDK中的SPI - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中阐述了JDK标准的SPI,并对其应用做了相应的实践.在实际应用中,很多框架都会对其进行扩展 ...

  5. 理解 Dubbo SPI 扩展机制

    写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...

  6. [转] 理解 Dubbo SPI 扩展机制

    写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...

  7. 详解Apache Dubbo的SPI实现机制

    一.SPI SPI全称为Service Provider Interface,对应中文为服务发现机制.SPI类似一种可插拔机制,首先需要定义一个接口或一个约定,然后不同的场景可以对其进行实现,调用方在 ...

  8. Dubbo中订阅和通知解析

    Dubbo中关于服务的订阅和通知主要发生在服务提供方暴露服务的过程和服务消费方初始化时候引用服务的过程中. 2345678910111213141516171819 public <T> ...

  9. Java中的SPI扩展机制(有demo)

    参考连接:https://www.jianshu.com/p/3a3edbcd8f24 一.什么是SPI SPI ,全称为 Service Provider Interface,是一种服务发现机制.它 ...

随机推荐

  1. 如何在服务器上配置ODBC来访问本机DB2 for Windows服务器

    如何在服务器上配置ODBC来访问本机 DB2 for Windows服务器                         马根峰             (广东联合电子服务股份有限公司, 广州 51 ...

  2. 导出excel 的方法及示例

    一.基本知识 1.Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 2. HSSF 是Horribl ...

  3. 利用HTML5,前端js实现图片压缩

    http://blog.csdn.NET/qazwsx2345/article/details/21827553 主要用了两个HTML5的 API,一个file,一个canvas,压缩主要使用cnav ...

  4. Android Gradle使用总结

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/77678577 本文出自[赵彦军的博客] 其他 Groovy 使用完全解析 http ...

  5. 简单而强大的bitset

    简单而强大的bitset 介绍 有些程序需要处理二进制有序集,标准库提供了bitset 类型,事实上,bitset 是一个二进制容器,容器中每一个元素都是一位二进制码,或为 0,或为 1. 基础 bi ...

  6. Dubbo学习-源码学习

    Dubbo概述 dubbo框架提供多协议远程调用,服务提供方可以是分布式部署.dubbo框架可以很简单的帮我们实现微服务. 此处援引官网上图片 dubbo分为客户端和服务提供方 服务方将服务注册到注册 ...

  7. 前后台分离部署时,Niginx上的部署

    upstream bowenpay_backend { server 127.0.0.1:9002; } server { listen 80; server_name wx.bowenpay.com ...

  8. leetCode刷题(使用链表做加法)

    Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8 Explanation: 342 + 465 = ...

  9. Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

    Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin[Finchley 版]  发表于 2018-04-24 |  随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请 ...

  10. 三级菜单python写法(递归写法)

    数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家':{}, ...