ExtensionLoader是dubbo SPI的核心,暴露的两个核心方法如下:
Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("dubbo");
首先分析下getExtensionLoader(Protocol.class).getAdaptiveExtension() 方法内部逻辑,主要分为两部分:
一. ExtensionLoader.getExtensionLoader(Class type)
此方法是一个静态方法,ExtensionLoader 内部定义了一个map,维护Class<---> ExtensionLoader 之间的对应关系,最开始Protocol.class这个类找不到的时候会new ExtensionLoader()并放入到map中。
ExtensionLoader 构造函数内部定义如下:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
对于非ExtensionFactory会再次调用ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取到ExtensionFactory的具体实现作为扩展点工厂类,这个由dubbo内部维护,会返回AdaptiveExtensionFactory。
二. ExtensionLoader.getAdaptiveExtension()
在前面一步得到ExtensionLoader实例之后调用getAdaptiveExtension(),这个方法内部主要分为两个步骤:
- getAdaptiveExtensionClass().newInstance(); // 得到 AdaptiveExtensionClass并实例化
- injectExtension(extensionInstance); // 为 extensionInstance 注入属性
1. getAdaptiveExtensionClass() 内部主要下面两个步骤
- getExtensionClasses(); // 负责从jar包中加载SPI 配置信息
- cachedAdaptiveClass = createAdaptiveExtensionClass(); //动态创建AdaptiveExtensionClass 源代码并编译成Class
getExtensionClasses()
getExtensionClasses() 调用关系如下
1. ExtensionLoader.getExtensionClasses();
1) this.cachedClasses = loadExtensionClasses();
2) this.cachedClasses;
2. ExtensionLoader.loadExtensionClasses()
1) Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
2) loadFile(extensionClasses,"META-INF/dubbo/internal/"); //!/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
3) loadFile(extensionClasses,"META-INF/dubbo/"); //!/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
4) loadFile(extensionClasses, "META-INF/services/"); //!META-INF/services/com.alibaba.dubbo.rpc.Filter
5) return extensionClasses;
Dubbo可以尝试中三个路径中loadClass, dubbo内置 的META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件内容如下:
registry=class com.alibaba.dubbo.registry.integration.RegistryProtocol
injvm=class com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
thrift=class com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
dubbo=class com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
mock=class com.alibaba.dubbo.rpc.support.MockProtocol
http=class com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
redis=class com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rmi=class com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
dubbo 直接通过Class.forName()的形式加载这些SPI的扩展类,并且缓存在cachedClasses,注意这是所有的ExtensionClass,和AdaptiveExtension 存在区别。
createAdaptiveExtensionClass()
createAdaptiveExtensionClass 方法内部逻辑如下
1) String code = createAdaptiveExtensionClassCode(); // 通过interface className 动态创建Source Code
2) ClassLoader classLoader = findClassLoader();
3) com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
4) return compiler.compile(code, classLoader);
createAdaptiveExtensionClassCode 通过SPI 接口动态产生Code,Protocol产生的AdaptiveExtension如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
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.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);
}
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);
}
}
可以看到内部最核心的一句:
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class) .getExtension(extName);
通过动态getExtension(extName)来load具体的spi实现。还记得前面的loadExtensionClasses定义的那个map吗?getExtension实际上就是从这个map中get具体的extension。
感想
看完所有代码 dubbo 无非希望动态通过name得到Extension,采用这么绕的方案,确实不太高明啊。
网友评论