为什么要了解Dubbo的扩展机制
Dubbo的设计中大量使用多态,通过Dubbo的扩展机制来决定调用方法的真正类型,所以想要了解Dubbo的源码是如何走下去的,一定要先懂得Dubbo的扩展机制。
Dubbo的扩展机制规范
- 如果要扩展自定义的SPI,需要在resources目录下进行如下的配置,ExtensionLoader在初始化所有类型的子类时会对以上路径扫描。
private static final StringSERVICES_DIRECTORY ="META-INF/services/"; private static final StringDUBBO_DIRECTORY ="META-INF/dubbo/"; private static final StringDUBBO_INTERNAL_DIRECTORY =DUBBO_DIRECTORY +"internal/";
- 文件的名称需要和接口保持一致
xxx=com.alibaba.xxx.XxxProtocol
- 需要在xml中进行配置
源码分析
- ExtensionLoader.getExtensionLoader(Class type)
这是初始化一个ExtensionLoader的入口,在Dubbo中通过ExtensionLoader来管理着不同的接口,如Container
、 Protocol
等,ExtensionLoader相当于维护着接口类型以及具体实现子类Map的容器。
public static ExtensionLoader getExtensionLoader(Class type) {
·····
//先从缓存中取值,为null,则去new一个ExtensionLoader
ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);
}
return loader;
}
EXTENSION_LOADERS
是管理ExtensionLoader的一个Map,当我们的ExtensionLoader没有进行过初始化的时候会来执行new()操作。在ExtensionLoader的构造方法里,维护了接口类型type
以及进行objectFactory
的初始化,针对type不是ExtensionFactory
的会把 `objectFactory
构造成如下的结构
- objectFactory=AdaptiveExtensionFactory;
- factories = SpiExtensionFactory 、SpringExtensionFactory
至此,一个ExtensionLoader
已经构造好了,接下来需要执行的方法是ExtensionLoader
的getAdaptiveExtension()方法
,通过这个方法我们就可以获得我们具体的实现类了,在dubbo中的成员变量就是这样生成,如
private static final Protocolprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
-
getAdaptiveExtension ( )
public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError == null) { synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t); } } } } else { throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } }
首先是一个双重检查锁来进行对象的加载,如果没有存在于缓存的话会
createAdaptiveExtension();
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
在getAdaptiveExtensionClass()
中是为了获取到具体实现类的Class,如果方法上有Adaptive
注解则会生成代理类,否则直接获取到Class对象。具体的代码逻辑如下:
private Class> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
首先还是通过双重检查获取的方式来看是否进行过某个type的处理,在loadExtensionClasses
中会对SPI
上的默认值进行处理,并且通过对类名去匹配具体实现类的文本文件.
private Map> getExtensionClasses() {
Map> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
接下来看一下方法:loadExtensionClasses()
// 此方法已经getExtensionClasses方法同步过。
private Map> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
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("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map> extensionClasses = new HashMap>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
DUBBO_INTERNAL_DIRECTORY
,DUBBO_DIRECTORY
,SERVICES_DIRECTORY
也就是在文章开始中说过的为什么要把文本放在如上目录,因为loadFile()
只会从如上三个路径进行匹配。
private void loadFile(Map> extensionClasses, String dir) {
// 1.判断当前class类上面有没有Adaptive注解,如果有,则直接赋值给cachedAdaptiveClass
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
//2.如果没有类注解,那么判断该class中没有参数是type的构造方法,如果有,则把calss放入cachedWrapperClasses中
try {
clazz.getConstructor(type);
Set> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//3.判断是否有默认构造方法
clazz.getConstructor();
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
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("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//4.判断class是否有@Activate注解,如果有,则放入cachedActivates
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
//5.缓存calss到cachedNames中
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
}
在上面的这个方法中,判断当前class类上面有没有Adaptive注解,如果有,则直接赋值给cachedAdaptiveClass,如果没有则需要通过Compiler来生成class,生成的代理类如下:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative 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.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 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);
}
}
为什么dubbo整个设计是根据url来进行驱动的,从上面生成的代理类可以看到,决定具体的实现类是什么都是通过url来进行传递的。
一点总结
- 每个ExtensionLoader和每个type(Container,Protocol) 是一个绑定的关系。
- ExtensionLoader中的 ExtensionFactory 是
AdaptiveExtensionFactory
,AdaptiveExtensionFactory
中维护了具体的SpiExtensionFactory
和SpringExtensionFactory
- 如果类上有Adaptive注解则不会生成代理类,
AdaptiveCompiler
、AdaptiveExtensionFactory
;在方法上有Adaptive则会生成代理类