美文网首页
Dubbo内核之SPI机制

Dubbo内核之SPI机制

作者: 千淘萬漉 | 来源:发表于2018-09-27 21:40 被阅读79次

    一、JDK的SPI机制

    SPI 的全名为 Service Provider Interface,java设计出SPI目的是为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。这样程序运行的时候,该机制就会为某个接口寻找服务的实现,有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。例如,JDBC驱动,可以加载MySQL、Oracle、或者SQL Server等,目前有不少框架用它来做服务的扩张发现。

    通过一张图来看看,用SPI需要遵循哪些规范,因为SPI毕竟是JDK的一种标准:

    JAVA SPI

    SPI约定:

    • 在工程的META-INF/services/目录下,以接口的全限定名作为文件名,文件内容为实现接口的服务类;
    • 使用ServiceLoader动态加载META-INF/services下的实现类;
    • 接口的实现类需含无参构造函数;(因为类默认包含无参构造函数,如果我们没有重载构造函数所以此处可忽略)

    如果在META-INF/services下有接口实现类,存在多个(例如jar包下面也有相应),系统如何处理?Jdk会全部加载,java.util.ServiceLoader在加载资源文件时,已经考虑了这个问题。譬如Mysql的驱动包就包含两个驱动:

    com.mysql.jdbc.Driver
    com.mysql.fabric.jdbc.FabricMySQLDriver
    

    mysql中驱动包的具体实现:

    java中的ServiceLoader就会根据该路径去加载装配模块。但是JDK的SPI实现有着以下缺点:

    • 获取实现类的方式只能通过Iterator遍历,不能通过具体参数来获取。
    • 接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。

    二、DUBBO-SP机制

    dubbo spi的目的也是一样:可以获取类的实例对象,和java的SPI机制非常相似,但是又增加了如下功能:

    • 可以方便的获取某一个想要的扩展实现,java的SPI机制就没有提供这样的功能
    • 增加了对扩展点IoC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。

    约定:
    在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。 (注意:这里的配置文件是放在你自己的jar包内,不是dubbo本身的jar包内,Dubbo会全ClassPath扫描所有jar包内同名的这个文件,然后进行合并)

    扩展Dubbo的协议示例:
    在协议的实现jar包内放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,内容为:

    xxx=com.alibaba.xxx.XxxProtocol
    

    实现内容为:

    package com.alibaba.xxx;
    
    import com.alibaba.dubbo.rpc.Protocol;
    public class XxxProtocol implemenets Protocol {
    
        // ...
    }
    

    1.dubbo源码分析

    dubbo的扩展点框架主要位于这个包下:com.alibaba.dubbo.common.extension
    大概结构如下:

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

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

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

    这三个方法将会是我们重点关注的方法;每一个ExtensionLoader实例仅负责加载特定SPI扩展的实现。因此想要获取某个扩展的实现,首先要获取到该扩展对应的ExtensionLoader实例,下面我们就来看一下获取ExtensionLoader实例的工厂方法getExtensionLoader

    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)) { // 只接受使用@SPI注解注释的接口类型
            throw new IllegalArgumentException("Extension type(" + type + 
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
    
        // 先从静态缓存中获取对应的ExtensionLoader实例
        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实例。

    接下来看下ExtensionLoader的私有构造函数:

    private ExtensionLoader(Class<?> type) {
        this.type = type;
    
        // 如果扩展类型是ExtensionFactory,那么则设置为null
        // 这里通过getAdaptiveExtension方法获取一个运行时自适应的扩展类型
        // (每个Extension只能有一个@Adaptive类型的实现,如果没有dubbo会动态生成一个类)
        objectFactory = (type == ExtensionFactory.class ? 
              null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
    

    这里保存了对应的扩展类型,并且设置了一个额外的objectFactory属性,是一个ExtensionFactory类型,ExtensionFactory主要用于加载扩展的实现:

    @SPI
    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注解注释,说明他也是一个扩展点,从前面com.alibaba.dubbo.common.extension包的结构图中可以看到,dubbo内部提供了两个实现类:SpiExtensionFactoryAdaptiveExtensionFactory,实际上还有一个SpringExtensionFactory,不同的实现可以已不同的方式来完成扩展点实现的加载,这块稍后再来学习。从ExtensionLoader的构造函数中可以看到,如果要加载的扩展点类型是ExtensionFactory,object字段被设置为null。由于ExtensionLoader的使用范围有限(基本上局限在ExtensionLoader中),因此对他做了特殊对待:在需要使用ExtensionFactory的地方,都是通过对应的自适应实现来代替。

    默认的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>();
            for (String name : loader.getSupportedExtensions()) { // 将所有ExtensionFactory实现保存起来
                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;
        }
    
    }
    

    扩展点加载机制(ExtensionLoader)
    dubbo 大白话系列-扩展点机制

    相关文章

      网友评论

          本文标题:Dubbo内核之SPI机制

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