框架 Dubbo 如何扩展SPI 介绍

225 阅读3分钟

Dubbo如何扩展Java的1个小知识点

1.Java SPI 与 Dubbo ExtensionLoader

  • 1.SPI全称Service Provider Interface是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。你可以简单的理解为Java提供了一个接口,这个接口可也由A厂商实现,也可以由B厂商实现。也就是Client调用接口时,Java会加载这个接口的所有的实现类。废话不多说上例子
  • 定义一个接口和两个类
``` Java
public interface Robot {
    void sayHello();
}
```
``` Java 实现Robot
public class Bumblebee implements Robot {
    public void sayHello() {
        System.out.println("hello ,I am Bumblebee");
    }
}
```
``` Java 实现Robot
public class OptimusPrime implements Robot {
    public void sayHello() {
        System.out.println("hello i am Optimus prime ");
    }
}
```
  • 接着在resources文件夹下对应创建目录services如图,并在里面吸入两个实现类的全类名

  • 程序执行结果
  • hello ,I am Bumblebee
  • hello i am Optimus prime
  • 原理解析
``` Java 原理分析
    Iterator iterator = load.iterator();
        while (iterator.hasNext()) {
            iterator.next().sayHello();
        }
```
  • 在ServiceLoader内部有一个内部类LazyIterator
  • 执行过程

  • private static final String PREFIX = "META-INF/services/";
  • 获取对应"META-INF/services/+对应接口的类全名
  • 如果有则通过pending = parse(service, configs.nextElement());进行解析按Iterator顺序返回对应的实现类。

  • 服务实现:一个是本地服务的加载,
  • 模式 使用策略模式+配置文件的方法记载对应接口的实现类。
  • JavaSPI的实现有什么坏处?
  • 1.全量加载服务实现
  • 2.没有缓存
  • 3.不可以单个Service Provider

Dubbo ExtensionLoader

  • 例子:
  • 类与接口 与Java Spi相同的实现类
  • 配置文件这里的不同点是beanName = 类全名
```Java
        ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);
        // 获取对应的元素
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
```
  • 程序运行结果
  • hello ,I am Bumblebee
  • hello i am Optimus prime
  • 执行原理分析
  • 调用过程ExtensionLoader
  • ExtensionLoader.getExtensionLoader(Robot.class);
  • com.alibaba.dubbo.common.extension.ExtensionLoader#getExtensionLoader()
  • com.alibaba.dubbo.common.extension.ExtensionLoader#ExtensionLoader()
  • 下面会先初始化ExtensionFactory中SpiFactory
  • objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
  • 加载完ExtensionFactory后就初始化业务类
  • 授信会加载对应的Classes
  • com.alibaba.dubbo.common.extension.ExtensionLoader#getExtensionClasses()
  • com.alibaba.dubbo.common.extension.ExtensionLoader#loadExtensionClasses()
```Java
      final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if ((value = value.trim()).length() > 0) {
                // 分割@SPI("impl1") 中的value
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                // 将value放到cachedDefaultName中
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }

        Map> extensionClasses = new HashMap>();
        // DUBBO_INTERNAL_DIRECTORY : "META-INF/dubbo/internal/"
        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        // DUBBO_DIRECTORY : "META-INF/dubbo/"
        loadDirectory(extensionClasses, DUBBO_DIRECTORY);
        // 这里会加载对应目录下的文件与JavaSpIy一致
        // SERVICES_DIRECTORY : "META-INF/services/"
        loadDirectory(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
```
  • 加载完类文件返回的结果如下

  • 接着获取对应的Class类,通过createExtension()获取对应的Interface Provider通过clazz.newInstance()最后返回实例 com.alibaba.dubbo.common.extension.ExtensionLoader#createExtension();

  • 高性能的体现ExtensionLoader中有不同的缓存。不论缓存Classes或者是Instance都是用的单例模式,双重校验的方法去做的即先从缓存中拿,拿不到则加载并创建,创建后放回缓存中。

```Java
 // 所有标注@SPI注解的 class->ExtensionLoader
    private static final ConcurrentMap, ExtensionLoader> EXTENSION_LOADERS = new ConcurrentHashMap, ExtensionLoader>();
    // 所有标注@SPI的实例
    private static final ConcurrentMap, Object> EXTENSION_INSTANCES = new ConcurrentHashMap, Object>();

    // ==============================
    // 当前Extension的类 类型
    private final Class type;
    // ExtensionFactory扩展类创建工厂; SpiExtensionFactory , AdaptiveExtensionFactory , SpringFactory
    private final ExtensionFactory objectFactory;
    // 当前类缓存的 class->String,拓展类(extension)的名字(name)标识
    private final ConcurrentMap, String> cachedNames = new ConcurrentHashMap, String>();
    // 缓存的extension实例
    private final Holder>> cachedClasses = new Holder>>();
    // 可自动激活的实现类,name->Activate,其中Activate包含了这个自动激活的类的各种信息,比如时序  这个不太懂
    private final Map cachedActivates = new ConcurrentHashMap();
    //
    private final ConcurrentMap> cachedInstances = new ConcurrentHashMap>();
    // 自适应类@Adaptive类自动加载
    private final Holder cachedAdaptiveInstance = new Holder();
    // 自适应类@Adaptice字节码文件
    private volatile Class cachedAdaptiveClass = null;
    // 缓存扩展类名称
    private String cachedDefaultName;
```

  • 可以通过BeanName去获取不同的Service Provider解决了JavaSpI的问题。
  • 加载的时候先先从缓存判断有无再进行加载而不是想JavaSpI中的全量加载。