从网上了解到要学习dubbo的源码,必须要从SPI开始,dubbo框架作者从设计之初,就作为一个高可扩的服务框架。
java SPI
全称(Service Provider Interface),主要作用是提供接口,不同厂商对接口可以有自己不同的实现。我们比较熟悉的就是java.sql.Driver接口。
当厂商提供实现之后,需要在classpath:META-INF/services/下创建服务接口名的文件,并把具体的实现类写进去。
MySql驱动mysql-connector-java中,META-INF/services/文件中就有一个java.sql.Driver文件,内容是:1
2com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
我们可以通过如下加载扩展点1
2
3
4
5ServiceLoader<接口> serviceLoader=ServiceLoader.load(接口.class);
for(接口 cmd:serviceLoader){
cmd.method();
}
官方文档对ServiceLoader类的功能介绍
dubbo不是使用java原生SPI机制
为什么不使用原生的?
- 原生的一次性加载所有扩展点实现,造成资源的浪费
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
- 原生的没有IOC和AOP的功能
- 原生的不支持缓存和默认值
dubbo SPI
ExtensionLoader类是dubbo实现SPI的核心类,dubbo的扩展都是通过调用ExtensionLoader的getExtension方法加载的,我们主要目的是通过他获取一个实现类。
getExtensionLoader 为该接口new一个ExtensionLoader,然后缓存起来。
将断点打在类com.alibaba.dubbo.container.Main的private static final ExtensionLoader<Container> loader = ExtensionLoader.getExtensionLoader(Container.class);
上
调用过程1
2
3
4
5
6
7ExtensionLoader.getExtensionLoader(Container.class)
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
-->this.type = type;
-->objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
-->ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
-->this.type = type;
-->objectFactory =null;
执行以上代码完成了2个属性的初始化
- 每个一个ExtensionLoader都包含了2个值 type 和 objectFactory
- Class<?> type;//构造器 初始化时要得到的接口名 (标示当前ExtensionLoader所属SPI接口)
- ExtensionFactory objectFactory//构造器 初始化时 (用于IOC注入) AdaptiveExtensionFactory[SpiExtensionFactory,SpringExtensionFactory]
- new 一个ExtensionLoader缓存在
ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS
需要特别注意ExtensionFactory也是SPI接口,唯一不同点是他的objectFactory=null
getAdaptiveExtension 获取自适应扩展点
Protocol protocol =ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();1
2
3
4
5
6
7
8
9
10-->getAdaptiveExtension()//为cachedAdaptiveInstance赋值
-->createAdaptiveExtension()
-->getAdaptiveExtensionClass()
-->getExtensionClasses()//为cachedClasses 赋值
-->loadExtensionClasses()
-->loadFile
-->createAdaptiveExtensionClass()//自动生成和编译一个动态的adpative类,这个类是一个代理类
-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
-->compiler.compile(code, classLoader)
-->injectExtension()//作用:IOC动态注入
- 主要复杂的过程loadFile:
- 读取dubbo定义的META-INF/dubbo/internal(dubbo内部默认实现全部放在这个目录,还有2个目录META-INF/dubbo/和META-INF/services/)下定义的当前需要加载的扩展点实现
- 如果定义的实现类被@Adaptive注解,缓存到cachedAdaptiveClass
- 只有当该class无adative注解,并且构造函数包含目标接口(type)类型,缓存到cachedWrapperClasses, 例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中
- 如果2和3都不满足,则判断当前实现类是否被@Activate注解,并缓存到cachedActivates中,并将类作为key,实现自定义简称作为val缓存到cachedNames(META-INF/dubbo/internal里面文件的内容是实现自定义简称=具体实现类)
META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol内容1
2
3filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
- createAdaptiveExtensionClass 只有当META-INF/dubbo/internal定义的实现类没有一个被@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
25
26
27
28
29
30
31
32
33
34
35
36package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
//没有打上@Adaptive的方法如果被调到抛异常
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!");
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
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.RpcException {
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);
}
}
Transporter接口生成的动态类如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adaptive implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.remoting.RemotingException {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
- 生成动态类名称为接口名$Adaptive
- 生成动态类的前提是SPI接口方法需要被@Adaptive注解,并且方法参数
必须包含URL或者参数有URL成员
(URL为dubbo自定义的,URL几乎贯穿整个SPI接口调用链),通过URL传入不同的参数动态选择不同的实现 - 采用哪种方式获取扩展名?
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
与String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
2种方式- 当@Adaptive(“xxx”)指定为protocol时走
url.getProtocol()
否则走url.getParameter(‘’,’默认’),默认值是@SPI(‘val’)中的val - 当默认@Adaptive不指定值时,以扩展点接口名为xxx.
- Protocol接口特殊,它默认方法是不指定值,2中它xxx=protocol,又与1中说的指定为Protocol,所有它走url.getProtocol()
- 当@Adaptive(“xxx”)指定为protocol时走
- compiler.compile(code, classLoader)
1.Compiler也是SPI接口,加载CompilerClassLoader classLoader = findClassLoader(); com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class)
默认采用javassist实现动态类生成
2.为什么选用javassist? 可以看下作者博客代理方案性能对比 - injectExtension
- 当objectFactory不为空是,实现类有set开头的方法,并且方法参数有SPI接口的方法就能进行注入
- objectFactory有2个主要实现SpringExtensionFactory和SpiExtensionFactory,SpiExtensionFactory注入的是自适应扩展点(@Adaptive注解的类或者是动态生成的),SpringExtensionFactory注入的是bean
getExtension(String name)通过扩展点名称获取扩展
1 | getExtension(String name) //指定对象缓存在cachedInstances;get出来的对象wrapper对象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一个。 |
- 和getAdaptiveExtension很多地方都是一样的,
- 有个不同就是
injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))
,当包装类含有当前type的构造函数时,创建此构造函数并进行注入,如果含有包装类,会对包装类进行链式包装(装饰模式),返回最后一个包装类。核心代码如下: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
26private 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);
}
//set方法依赖注入
injectExtension(instance);
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) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
注解
@SPI
1 |
|
- 只有在接口打了@SPI注解的接口类才会去查找扩展点实现
@Adaptive
1 |
|
- 注解在类上作为一个自适应扩展点
- 注解在方法上,用于动态生成代理类
@Activate
1 |
|
- 对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同时加载多个实现,此时,可以用自动激活来简化配置
- 可以通过getActivateExtension(URL url, String key)获取激活扩展点集合