源码分析基于dubbo 2.7.1-release
看dubbo源码不得不了解dubbo spi机制,因为你常常看到如下代码,而debug又不知所踪
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
dubbo spi机制增强了Java 原生的 SPI 机制。简单来说,ExtensionLoader支持某个扩展接口存在多个扩展类(如Protocol,ProxyFactory),并通过键值对的方式进行配置,dubbo优先使用用户配置扩展类,如果用户没有配置,就使用默认扩展类。
先看看ExtensionLoader.getExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
...
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;
}
比较简单,就是从缓存中获取,获取失败则创建一个ExtensionLoader。
再看看getAdaptiveExtension,同样查询缓存,缓存查询失败则调用createAdaptiveExtension生成一个扩展类Extension
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
...
}
}
injectExtension负责调用扩展类Extension的set方法,注入对应属性。
getAdaptiveExtensionClass获取扩展类的Class,再通过newInstance实例化一个对象。
查看getAdaptiveExtensionClass方法
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) { // 检查缓存
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
getExtensionClasses会检查cachedClasses,如果cachedClasses为空,它会调用loadExtensionClasses,加载用户配置的扩展类,并存放到cachedClasses中。
private Map<String, Class<?>> loadExtensionClasses() {
...
Map<String, Class<?>> extensionClasses = new HashMap<>();
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName());
loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName());
loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));
return extensionClasses;
}
变量DUBBO_DIRECTORY的值为META-INF/dubbo/
,我们自定义的扩展配置就是放到该路径下。
type.getName().replace("org.apache", "com.alibaba")
是因为dubbo成为apace项目后,修改pageage。
loadDirectory会读取指定目录下的配置文件,加载class后存放到cachedClasses。
如dubbo-rpc\dubbo-rpc-dubbo\src\main\resources\META-INF\dubbo\internal\org.apache.dubbo.rpc.Protocol
内容为:
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
cachedClasses中的map中会添加dubbo:Class<DubboProtocol>的键值对。
createAdaptiveExtensionClass会生成一个通用的ExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 生成代码字符串
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
// 编译成class
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 编译为Class
return compiler.compile(code, classLoader);
}
AdaptiveClassCodeGenerator会拼凑生成代码字符串,并通过Compiler编译成class。
AdaptiveClassCodeGenerator这里不深入,主要是通过接口的@SPI,@Adaptive注解获取对应的默认配置。
如Protocol接口
@SPI("dubbo")
public interface Protocol {
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
...
}
生成的代码如下
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
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);
}
...
}
关键代码是String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() )
,这里通过invoker.url中的protocol,再通过getExtension方法找到对应的实现类。如dubbo://...
, registry://...
分别对应DubboProtocol
,RegistryProtocol
。
默认使用dubbo协议,这个默认值正是从Protocol接口的@SPI("dubbo")
注解中获取
这是一个代理类,当我们调用该类的export方法,会通过ExtensionLoader.getExtension获取真正逻辑类,并调用逻辑类export方法。
最后看看getExtension
public T getExtension(String name) {
...
// 获取Holder
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
// 获取instance
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
最后看看createExtension
private T createExtension(String name) {
// 获取ExtensionClass
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 初始化ExtensionClass
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);
// 获取wrapperClasse
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);
}
}
getExtensionClasses会查询cachedClasses,前面已经将配置中的Extension信息添加到这里了。
这里要注意,如果存在wrapperClasses,会创建对应的wrapperClasses来装饰目标类。wrapperClasses就是典型的装饰模式。
如Protocol就有ProtocolListenerWrapper,ProtocolFilterWrapper,QosProtocolWrapper等装饰类。
ExtensionLoader加载扩展类时,如果一个扩展类有一个构造方法,参数是它的扩展接口,就会加入到cachedWrapperClasses中。
网友评论