Dubbo源码学习之-SPI介绍

前言java

学习之路仍是要戒骄戒躁,一以贯之的积累前行。以前的公司部门技术达人少,本身总向往那些技术牛人多的团队,想象本身进去以后能跟别人学到多少东西。现在进到一个这样的团队以后,却发现以前本身的想法过于幼稚。且不说因为人与人之间性格不合致使的难以深刻相处,即便相处融洽,别人也不会给你太多的帮扶,更多的仍是靠本身去学习去探究。学习的道路上没有什么捷径,且会有不少的心魔须要本身去克服。闲话少叙,今天主要是说一下Dubbo中SPI的基本内容,自适应拓展的部分后面单独成文。spring

什么是SPI缓存

要说Dubbo的SPI,则必须先说说Java原生的SPI。可能不少道友都没有据说过SPI,它是Service Provider Interface 即服务提供接口的简称,顾名思义,它就是用来提供服务的。app

在Java中是如何提供服务的呢?简要来讲,就是在资源文件目录下(即resource目录下)的META-INF/services文件夹下,创建文件名为接口的全路径名的文件,文件内容为此接口的实现类全路径名。而后在代码中经过ServiceLoader类获取这些配置的实现类,而后就能够自由的使用这么实现类了。下面是我在本地写的一个小Demo:框架

代码结构以下所示:ide

接口代码:学习

1 package spipackage;
2 public interface SpiInterface {
3     void getName();
4 }

两个实现类代码:测试

1 package spipackage;
2 public class SpiImpl implements SpiInterface{
3     @Override
4     public void getName() {
5         System.out.println("SpiImpl");
6     }
7 }
1 package spipackage;
2 public class SpiImplTwo implements SpiInterface {
3     @Override
4     public void getName() {
5         System.out.println("SpiImplTwo");
6     }
7 }

资源文件:spa

1 spipackage.SpiImpl
2 spipackage.SpiImplTwo

测试类:设计

 1 package spipackage;
 2 import java.util.Iterator;
 3 import java.util.ServiceLoader;
 4 public class SpiTestClient {
 5     public static void main(String[] args) {
 6         ServiceLoader<SpiInterface> spiInterfaces = ServiceLoader.load(SpiInterface.class);
 7         // 循环调用实现类中的方法
 8         spiInterfaces.forEach(SpiInterface::getName);
 9         // 获取某个实现类进行调用
10         Iterator<SpiInterface> iterator = spiInterfaces.iterator();
11         while (iterator.hasNext()) {
12             SpiInterface next = iterator.next();
13             if (next instanceof SpiImplTwo) {
14                 next.getName();
15             }
16         }
17     }
18 }

测试结果:

什么是Dubbo的SPI

从java原生SPI的使用上可知,它是一次性加载整个资源文件中的数据,当你要获取其中某个实现类时也只能经过遍从来获得。而Dubbo的开发人员们显然要让其更加灵活,因此Dubbo中的SPI是在Java原生SPI基础上作了改造升级。首先能够按需加载,须要用哪一个就加载哪一个,这是经过键值对来配置实现类作到的,至关于给每一个实现类打上了标签;其次还实现了依赖注入,即若是实现类A中须要注入实现类B,则dubbo在获取实现类A时会自动将B注入进去。

具体的本地代码测试跟上述相似,此处就不在贴出来了,只是需将ServiceLoader换成Dubbo的ExtensionLoader,且接口需带有@SPI注解,而且资源文件也可放入META-INF/dubbo目录下。

下面简要讲一下ExtensionLoader中的源码实现。Dubbo的ExtensionLoader类中,获取服务类的主要方法是getExtension方法,而在这个方法中,核心方法是createExtension,此方法很重要,代码以下所示:

 1 private T createExtension(String name) {
 2         // 一、先获取class类
 3         Class<?> clazz = getExtensionClasses().get(name);
 4         if (clazz == null) {
 5             throw findException(name);
 6         }
 7         try {
 8             T instance = (T) EXTENSION_INSTANCES.get(clazz);
 9             if (instance == null) {
10                 // 二、经过反射建立实例,且存入缓存
11                 EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
12                 instance = (T) EXTENSION_INSTANCES.get(clazz);
13             }
14             // 三、注入依赖,相似spring的依赖注入
15             injectExtension(instance);
16             // 四、将扩展对象包进wrapper对象中
17             Set<Class<?>> wrapperClasses = cachedWrapperClasses;
18             if (CollectionUtils.isNotEmpty(wrapperClasses)) {
19                 for (Class<?> wrapperClass : wrapperClasses) {
20                     instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
21                 }
22             }
23             return instance;
24         } catch (Throwable t) {
25             throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
26                     type + ") couldn't be instantiated: " + t.getMessage(), t);
27         }
28     }

分四步获取了类的实例对象。其中第一步中包含了主要的逻辑,它读取配置文件,是经过类加载器加载文件获取输入流,而后一行一行读取的,其中包括了对空格的处理、对注释的处理。以前总感受读取配置文件的实现很神奇,如今慢慢的能够一窥其中究竟了,以为也没多高大上,都是很实际的操做。

小结:SPI的做用

经过SPI实现的功能扩展,更相似于插拔式的扩展。增长了某些功能类以后,经过配置文件引入,而后在某些地方获取,调用便可。SPI机制是Dubbo的基础,了解了它才能更加清楚的看清Dubbo的框架设计。另外,经过对SPI的了解,我的感受SPI有点相似于Spring的IOC实现,也能够说Spring经过XMl配置文件或者注解实现了一种另类的SPI机制,让你不用关注实例对象的建立,只是用的时候获取到用便可,固然Spring实现的功能内容更多更易于扩展。

只要天天都有进步,都在朝目标前行,就可心安。戒骄戒躁,努力前行!

相关文章
相关标签/搜索