0%

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

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

jdk-SPI (Service Provider Interface)

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

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

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

dubbo-SPI

特性:

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

代码结构如下:

1
com.alibaba.dubbo.common.extension  
2
 |  
3
 |--factory  
4
 |     |--AdaptiveExtensionFactory     
5
 |     |--SpiExtensionFactory    
6
 |     |--SpringExtensionFactory      
7
 |  
8
 |--support  
9
 |     |--ActivateComparator  
10
 |  
11
 |--Activate  #自动激活加载扩展的注解  
12
 |--Adaptive  #自适应扩展点的注解  
13
 |--ExtensionFactory  #扩展点对象生成工厂接口  
14
 |--ExtensionLoader   #扩展点加载器,扩展点的查找,校验,加载等核心逻辑的实现类  
15
 |--SPI   #扩展点注解

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

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

    ExtensionLoader

    想要获取某个扩展的实现,首先要获取到该扩展对应的ExtensionLoader实例。
    1
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    2
            if (type == null)
    3
                throw new IllegalArgumentException("Extension type == null");
    4
            if (!type.isInterface()) { //接口判断
    5
                throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    6
            }
    7
            if (!withExtensionAnnotation(type)) { //注解判断
    8
                throw new IllegalArgumentException("Extension type(" + type +
    9
                        ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    10
            }
    11
            //从静态缓存中获取
    12
            ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    13
            if (loader == null) {
    14
                //为Extension类型创建ExtensionLoader实例
    15
                EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
    16
                loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    17
            }
    18
            return loader;
    19
    }
    上述方法需要一个Class类型的参数,该参数表示希望加载的扩展点类型,该参数必须是接口,且该接口必须被@SPI注解注释,否则拒绝处理。检查通过之后首先会检查ExtensionLoader缓存中是否已经存在该扩展对应的ExtensionLoader,如果有则直接返回,否则创建一个新的ExtensionLoader负责加载该扩展实现,同时将其缓存起来。所以对于每一个扩展,dubbo中只会有一个对应的ExtensionLoader实例。
    1
    private ExtensionLoader(Class<?> type) {
    2
            this.type = type;
    3
            objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    4
    }
    从ExtensionLoader的构造函数中可以看到,如果要加载的扩展点类型是ExtensionFactory是,objectFactory字段被设置为null。由于ExtensionLoader的使用范围有限(基本上局限在ExtensionLoader中),因此对他做了特殊对待:在需要使用ExtensionFactory的地方,都是通过对应的自适应实现来代替。
    objectFactory属性是一个ExtensionFactory类型,ExtensionFactory主要用于加载扩展的实现:
    1
    public interface ExtensionFactory {
    2
    3
        /**
    4
         * Get extension.
    5
         *
    6
         * @param type object type.
    7
         * @param name object name.
    8
         * @return object instance.
    9
         */
    10
        <T> T getExtension(Class<T> type, String name);
    11
    12
    }
    ExtensionFactory被@SPI注解注释,说明他也是一个扩展点,dubbo内部提供了三个实现类:SpiExtensionFactory 、AdaptiveExtensionFactory以及SpringExtensionFactory,不同的实现可以已不同的方式来完成扩展点实现的加载。
    默认的ExtensionFactory实现中,AdaptiveExtensionFactotry被@Adaptive注解,它就是ExtensionFactory对应的自适应扩展实现(每个扩展点最多只能有一个自适应实现,如果所有实现中没有被@Adaptive注释的,那么dubbo会动态生成一个自适应实现类),也就是说,所有对ExtensionFactory调用的地方,实际上调用的都是AdpativeExtensionFactory,那么我们看下他的实现代码:
    1
    @Adaptive
    2
    public class AdaptiveExtensionFactory implements ExtensionFactory {
    3
    4
        private final List<ExtensionFactory> factories;
    5
    6
        public AdaptiveExtensionFactory() {
    7
            ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
    8
            List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
    9
            // 将所有ExtensionFactory实现保存起来  
    10
            for (String name : loader.getSupportedExtensions()) {
    11
                list.add(loader.getExtension(name));
    12
            }
    13
            factories = Collections.unmodifiableList(list);
    14
        }
    15
    16
        public <T> T getExtension(Class<T> type, String name) {
    17
            // 依次遍历各个ExtensionFactory实现的getExtension方法,一旦获取到Extension即返回  
    18
            // 如果遍历完所有的ExtensionFactory实现均无法找到Extension,则返回null
    19
            for (ExtensionFactory factory : factories) {
    20
                T extension = factory.getExtension(type, name);
    21
                if (extension != null) {
    22
                    return extension;
    23
                }
    24
            }
    25
            return null;
    26
        }
    27
    28
    }
    他会遍历当前系统中所有的ExtensionFactory实现来获取指定的扩展实现,获取到扩展实现或遍历完所有的ExtensionFactory实现。这里调用了ExtensionLoader的getSupportedExtensions方法来获取ExtensionFactory的所有实现,又回到了ExtensionLoader类。

方法代码分析

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

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

1
com.alibaba.dubbo.common.extensionloader.ExtensionLoaderTest

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