Dubbo的适配器原理源码解析(Dubbo增强SPI)

167 阅读3分钟

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,源码到这里就分析结束了,谢谢~