美文网首页
dubbo-SPI扩展点加载机制简解

dubbo-SPI扩展点加载机制简解

作者: 南宋临安府 | 来源:发表于2018-03-08 23:55 被阅读0次

“天长地久。
天地所以能长且久者,以其不自生,故能长生。
是以圣人后其身而身先,外其身而身存。
非以其无私耶?
故能成其私。”[1]

jdk-SPI (Service Provider Interface)

SPI (Service Provider Interface),一种服务发现机制,为某个接口寻找服务实现的机制。dubbo-SPI就是基于此并有所加强。
在dubbo的源码中,像如下代码段会随处可见:

ExtensionLoader.getExtensionLoader(WrappedExt.class).getExtension("XXX");

这便是duboo-SPI的使用方式。

dubbo-SPI

特性:

  • 自动包装
  • 自动装配
  • 自适应
  • 自动激活

代码结构如下:

com.alibaba.dubbo.common.extension  
 |  
 |--factory  
 |     |--AdaptiveExtensionFactory     
 |     |--SpiExtensionFactory    
 |     |--SpringExtensionFactory      
 |  
 |--support  
 |     |--ActivateComparator  
 |  
 |--Activate  #自动激活加载扩展的注解  
 |--Adaptive  #自适应扩展点的注解  
 |--ExtensionFactory  #扩展点对象生成工厂接口  
 |--ExtensionLoader   #扩展点加载器,扩展点的查找,校验,加载等核心逻辑的实现类  
 |--SPI   #扩展点注解

需要我们最为关心的就是ExtensionLoader,几乎所有特性都在这个类中实现。
ExtensionLoader没有提供public的构造方法,但是提供了一个public static的getExtensionLoader,这个方法就是获取ExtensionLoader实例的工厂方法。其public成员方法中有三个比较重要的方法:

  • getActivateExtension :根据条件获取当前扩展可自动激活的实现
  • getExtension : 根据名称获取当前扩展的指定实现
  • getAdaptiveExtension : 获取当前扩展的自适应实现

ExtensionLoader

想要获取某个扩展的实现,首先要获取到该扩展对应的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 interface!");
        }
        if (!withExtensionAnnotation(type)) { //注解判断
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        //从静态缓存中获取
        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            //为Extension类型创建ExtensionLoader实例
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
}

上述方法需要一个Class类型的参数,该参数表示希望加载的扩展点类型,该参数必须是接口,且该接口必须被@SPI注解注释,否则拒绝处理。检查通过之后首先会检查ExtensionLoader缓存中是否已经存在该扩展对应的ExtensionLoader,如果有则直接返回,否则创建一个新的ExtensionLoader负责加载该扩展实现,同时将其缓存起来。所以对于每一个扩展,dubbo中只会有一个对应的ExtensionLoader实例。

private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

从ExtensionLoader的构造函数中可以看到,如果要加载的扩展点类型是ExtensionFactory是,objectFactory字段被设置为null。由于ExtensionLoader的使用范围有限(基本上局限在ExtensionLoader中),因此对他做了特殊对待:在需要使用ExtensionFactory的地方,都是通过对应的自适应实现来代替。
objectFactory属性是一个ExtensionFactory类型,ExtensionFactory主要用于加载扩展的实现:

public interface ExtensionFactory {

    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);

}

ExtensionFactory被@SPI注解注释,说明他也是一个扩展点,dubbo内部提供了三个实现类:SpiExtensionFactory 、AdaptiveExtensionFactory以及SpringExtensionFactory,不同的实现可以已不同的方式来完成扩展点实现的加载。
默认的ExtensionFactory实现中,AdaptiveExtensionFactotry被@Adaptive注解,它就是ExtensionFactory对应的自适应扩展实现(每个扩展点最多只能有一个自适应实现,如果所有实现中没有被@Adaptive注释的,那么dubbo会动态生成一个自适应实现类),也就是说,所有对ExtensionFactory调用的地方,实际上调用的都是AdpativeExtensionFactory,那么我们看下他的实现代码:

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        // 将所有ExtensionFactory实现保存起来  
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    public <T> T getExtension(Class<T> type, String name) {
        // 依次遍历各个ExtensionFactory实现的getExtension方法,一旦获取到Extension即返回  
        // 如果遍历完所有的ExtensionFactory实现均无法找到Extension,则返回null
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

他会遍历当前系统中所有的ExtensionFactory实现来获取指定的扩展实现,获取到扩展实现或遍历完所有的ExtensionFactory实现。这里调用了ExtensionLoader的getSupportedExtensions方法来获取ExtensionFactory的所有实现,又回到了ExtensionLoader类。

方法代码分析

  • getExtension
getExtension(name)  
    -> createExtension(name) #如果无缓存则创建  
        -> getExtensionClasses().get(name) #获取name对应的扩展类型  
        -> 实例化扩展类  
        -> injectExtension(instance) # 扩展点注入  
        -> instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)) #循环遍历所有wrapper实现,实例化wrapper并进行扩展点注入
  • getAdaptiveExtension
public T getAdaptiveExtension()  
    -> createAdaptiveExtension() #如果无缓存则创建  
        -> getAdaptiveExtensionClass().newInstance() #获取AdaptiveExtensionClass  
            -> getExtensionClasses() # 加载当前扩展所有实现,看是否有实现被标注为@Adaptive  
            -> createAdaptiveExtensionClass() #如果没有实现被标注为@Adaptive,则动态创建一个Adaptive实现类  
                -> createAdaptiveExtensionClassCode() #动态生成实现类java代码  
                -> compiler.compile(code, classLoader) #动态编译java代码,加载类并实例化  
        -> injectExtension(instance)  

能在本地把代码跑起来,debug状态能更好的跟踪代码的执行,dubbo源代码中提供了非常详细的测试用例,基本涵盖了所有的使用场景,可以本地试试,大有裨益!

com.alibaba.dubbo.common.extensionloader.ExtensionLoaderTest

  1. 老子《道德经》第七章,老子故里,中国鹿邑。

相关文章

  • dubbo-SPI扩展点加载机制简解

    “天长地久。天地所以能长且久者,以其不自生,故能长生。是以圣人后其身而身先,外其身而身存。非以其无私耶?故能成其私...

  • springboot SPI 扩展机制

    springboot的扩展解耦,仿照java的SPI机制,使用SpringFactoriesLoader加载spr...

  • dubbo的spi机制

    dubbo的spi机制 dubbo的扩展点加载机制源自于java的spi扩展机制。那么,何为java的spi扩展机...

  • ExtensionLoader解析

    版本 2.5.7 ExtensionLoader机制 Dubbo 的扩展点加载从 JDK 标准的 SPI (Ser...

  • dubbo中的SPI扩展机制

    dubbo官网对其SPI扩展机制的介绍如下: Dubbo 的扩展点加载从 JDK 标准的 SPI (Service...

  • 源码修炼笔记之Dubbo扩展点机制详解

    Dubbo具有良好的扩展性,其扩展性依赖于dubbo使用的各种设计模式和扩展点加载机制,本文对Dubbo SPI加...

  • Dubbo之SPI源码分析

    1.Dubbo SPI介绍 Dubbo的扩展点加载机制,用于在程序运行时,通过参数的不同,加载同一接口的不同实现。...

  • dubbo-SPI机制

    借用dubbo官网介绍的设计图,如下: ** 说明:图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之...

  • Dubbo SPI 机制解析

    从上一篇 Java SPI 机制解析 可以知道 Java SPI 的一些劣势。Dubbo 的扩展点加载从 Java...

  • 🍎 Spring 扩展点应用之工厂加载机制

    Spring 工厂加载机制,即 Spring Factories Loader,核心逻辑是使用 SpringFac...

网友评论

      本文标题:dubbo-SPI扩展点加载机制简解

      本文链接:https://www.haomeiwen.com/subject/xjyufftx.html