Dubbo在设计之初,为了架构的灵活性,在它架构中的每层为每个功能点都提供了一个SPI扩展接口(Dubbo增强SPI),Dubbo框架在使用扩展点功能的时候是对接口进行依赖,而一个扩展接口对应了一系列的扩展实现类,那么如何选择具体的实现类呢?让我们来看看源码吧!
我们以Protocol接口为例:可以看到SPI注解中的值为dubbo,Dubbo在运行过程中会使用动态编译技术为接口Protocol生成一个适配器类Protocol$Adaptive的实例,而这个实例的实现就对应SPI注解中所配置的dubbo对应的类的实现(具体是在META-INFO/dubbo、META-INFO/dubbo/internal、META-INFO/services这三个文件夹下的配置中配置的实现类),下面让我们看看Dubbo到底是怎么生成这个Adaptive的实例的吧!
@SPI("dubbo")public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
default List<ProtocolServer> getServers() { return Collections.emptyList(); }
}
在Provider方我们会用到一个类叫ServiceConfig,而在ServiceConfig中维护了一个静态内部常量protocol,它使用了ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()来获取是配置对象。
我们进入getExtensionLoader方法中可以看到这是一个静态方法,并且它为每一个Dubbo扩展接口都维护了一个ExtensionLoader实例,通过这个方法可以拿到对应Dubbo扩展接口的ExtensionLoader。
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 根据扩展接口的class来获取loader
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
然后通过调用ExtensionLoader的实例方法getAdaptiveExtension()来获取适配器实例,这里的调用链比较长。
首先ExtensionLoader中维护了一个实例的缓存cachedAdaptiveInstance,先判断缓存中是否存在,如果存在直接返回,然后通过双重校验的方式保证只会实例化一次,通过调用createAdaptiveExtension()方法来创建实例。
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("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
进入到createAdaptiveExtension()方法,这里的injectExtension方法主要是为Extension$Adaptive实例注入依赖,而构建实例的方法是getAdaptiveExtensionClass().newInstance(),其中getAdaptiveExtensionClass()是生成动态编译的类,将该类注入到对应的ClassLoader中,而生成实例则是调用newInstance()方法。
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
接下来进入到getAdaptiveExtensionClass()方法中,看看动态编译的类是怎么生成的。
可以看到Dubbo会先从META-INF路径下读取配置文件并加载class文件,在这里面会读取扩展接口上的@SPI的值,这里有个不一样的地方,大家知道这个适配器是为了选择自己想要的扩展,但是Dubbo将实现这个功能中的Compiler(动态编译)也弄成了一个扩展,如果按照之前设计的走向,那么就会变成套娃无限循环,就是选择Compiler的一个实例需要一个Compiler实例去动态编译它,所以Dubbo又扩展了一个注解@Adaptive,也就是这里的第二步,Adaptive注解的作用用通俗的说法就是适配器的适配器,拿Compiler举例,@SPI的适配器并不会选择最终的Compiler去实例化,而是会选择打上了Adaptive注解的Compiler去实例化,而这个打上了Adaptive注解的类会去实例化我们最终选择的Compiler类,可以在dubbo-common项目的META-INFO/dubbo.internal文件夹中找到它,而这个AdaptiveCompiler的compile方法会再去获取一次SPI注解上配置的Compiler,然后用这个Compiler进行动态编译。
// ExtensionLoader.java
private Class<?> getAdaptiveExtensionClass() {
// 1 从META-INF路径下的文件中读取定义的class文件
// 这里会从几个固定的路径去读取配置文件信息
getExtensionClasses();
// 2 如果存在@Adaptive注解的类,则优先使用这个类
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 3 通过扩展接口上的注解配置名称来创建Adaptive的Class对象
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
// 获取Compiler,如果没有Compiler则会先初始化Compiler
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
// ExtensionLoader.java
private Map<String, Class<?>> loadExtensionClasses() {
// 获取SPI注解上的扩展名
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
// 从固定的几个路径中读取配置的class
// loadDirectory中会处理Adaptive注解的class,会将其缓存到cachedAdaptiveClass变量中
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;
}
// AdaptiveCompiler.java
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER;
// copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
// 这里获取@SPI上配置的Compiler
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
所以可以看到,在第一次获取SPI的实例对象的时候,预加载完class文件后,如果没有找到对应的Adaptive类则会进行class的构建,而第一次构建的时候Compiler并没有被初始化,这个时候会先走一遍Compiler的初始化,将AdaptiveCompiler作为Compiler的对应实例,然后通过AdaptiveCompiler去初始化Compiler接口SPI注解中的对应实例,最后通过Compiler对需要的实例类进行初始化,最后通过newInstance获取实例。
ok,源码到这里就分析结束了,谢谢~