什么是spi
dubbo的所有功能都拆分并且抽象为interface,通过SPI查找这些interface的实现,并且通过url组合起来,就成了一个完整的rpc框架。SPI可理解为根据接口,找不同的实现类。
为什么要重新造一个
那么JDK已经有了SPI,dubbo为何要自己写一个?有没比SPI更好的机制去加载接口的实现类?
前面我们写了一个JDK的SPI例子,那么它有什么不好呢?
1.JDK中spi是一次性加载所有扩展,不能按需,有点浪费。 2.dubbo增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点
背景知识
扩展点自动包装
ExtensionLoader.getExtension()返回不一定是扩展点,有可能返回扩展点实现类的包装类Wrapper。
那什么时候会出现这种情况呢?
只有当该class无adative注解,并且构造函数包含目标接口(type)类型
例如:ProtocolFilterWrapper,ProtocolListenerWrapper.它会存在cachedWrapperClasses中。
复制代码
这其实是AOP的思想。比如下面这样,我们可在调用实际方法前后做一些额外操作。可以将一些公共逻辑抽取出来,放在wrapper类中。
public class XxxProtocolWrapper implemenets Protocol {
Protocol impl;
public XxxProtocol(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
}
复制代码
扩展点自动装配
加载扩展点时,自动注入依赖的扩展点。当扩展点实现类的成员是其他扩展点,那么ExtensionLoader会自动注入依赖的扩展点。
这不就是ioc嘛。!!!!
那么它是怎么做到的呢?
它通过扫描扩展点的所有Setter方法来判定其成员。那么如果这个依赖的扩展点有多个实现,该用哪个呢? 这就引出了了扩展点自适应。
扩展点自适应
在上面装备的过程中,其实注入的依赖是一个Adative实例。直到扩展点方法执行时才决定调用是一个扩展点实现。
那么Adative实例是怎么生成的呢?它又是怎么拿到具体对象的呢?
看下面的分析。
扩展点自动激活
本文讨论dubbo SPI机制中ExtensionLoader.getExtensionLoade(Class type)方法 1.dubbo SPI机制是什么?有什么用?怎么实现的? 2.与JDK SPI机制的对比 3.
ExtensionLoader.getExtensionLoader(Classtype)本质就是根据传入的形参,获取一个该类型的ExtensionLoader,然后缓存到EXTENSION_LOADERS,下次直接get。 举个例子:type为com.alibaba.dubbo.container.Container-----> com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.container.Container] dubbo SPI
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
//...去掉了if判断逻辑
ExtensionLoader<T> loader = EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(
type, new ExtensionLoader<T>(type));//私有构造方法
loader = EXTENSION_LOADERS.get(type);
}
return loader;
}
private ExtensionLoader(Class<?> type) {
this.type = type;//扩展点的接口类型
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class)
.getAdaptiveExtension());
}
复制代码
ExtensionLoader的构造方法是私有的,涉及到单例模式。 在这个构造方法中,首先会判断type是否是ExtensionFactory,若是,objectFactory=null。否则获取ExtensionFactory的扩展。
只要不是ExtensionFactory都会走到这里,得到ExtensionFactory的adaptive扩展(AdaptiveExtensionFactory)。这样一个ExtensionLoader就new好了。
每个ExtensionLoader包含type,objectFactory
两个元素。type是对应的接口,objectFactory就是定值。(AdaptiveExtensionFactory)
objectFactory这个变量到底是什么呢? 1.在ExtensionLoader是这样定义的;
private final ExtensionFactory objectFactory
复制代码
它是一个ExtensionFactory。
2.通过
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取。
3.它就是为dubbo的IOC提供所有对象。
下面我们以Protocol
为例,debug下代码
这里我们拿到了protocol的ExtensinoLoader。
下面获取adaptive
此时,我们已经拿到了extensionClasses.
因为Protocol没有对应实现类中含有@Adapive注解,但是Protocol的接口方法含有@Adapive注解,所以会走到这一步。
这里会根据定义好的模版生成代码。即生成有@Adapive注解的方法
下面开始就是发生ioc的地方。遍历动态生成的类,找到以set开头的方法。如果有,则注入AdaptiveExtensionFactory对象。这里设计的很巧妙。注入的是一个万能的对象,等到真正需要时,在决定要哪个。
到这里我们的getAdaptiveExtension就返回了。返回的是
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
private static final org.apache.dubbo.common.logger.Logger logger = org.apache.dubbo.common.logger.LoggerFactory
.getLogger(ExtensionLoader.class);
private java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(0);
public void destroy() {
throw new UnsupportedOperationException(
"method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException(
"method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
}
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + url.toString()
+ ") use keys([protocol])");
}
org.apache.dubbo.rpc.Protocol extension = null;
try {
extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
.getExtension(extName);
} catch (Exception e) {
if (count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + extName
+ " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", e);
}
extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
.getExtension("dubbo");
}
return extension.export(arg0);
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" + url.toString()
+ ") use keys([protocol])");
}
org.apache.dubbo.rpc.Protocol extension = null;
try {
extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
} catch (Exception e) {
if (count.incrementAndGet() == 1) {
logger.warn("Failed to find extension named " + extName
+ " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.", e);
}
extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension("dubbo");
}
return extension.refer(arg0, arg1);
}
}
复制代码
下面我们看如何根据name,获取Extension。
?,首先看InjvmProtocol中是否有set方法,如有,则注入Adaptive类。
继续。这里Protocol接口有两个wrapperClasses实例。它的作用是完成Aop。我们看下官网的解释。
对应这里,就是每个wrapperClasses实例,都注入Protocol的实现.
for循环。第一步将InjvmProtocol放到ProtocolListenerWrapper中。 第二步,为ProtocolListenerWrapper放入adaptive 。
继续循环,进入到
ProtocolFilterWrapper
。这里将会给第一步得到的ListenerWrapper包装。
然后,注入adaptive.
现在我们拿到了instance.整个流程走完了。不容易。设计的太巧妙了。