前言

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

什么是SPI

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

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

代码结构如下所示:

接口代码:

 package spipackage;
public interface SpiInterface {
void getName();
}

两个实现类代码:

 package spipackage;
public class SpiImpl implements SpiInterface{
@Override
public void getName() {
System.out.println("SpiImpl");
}
}
 package spipackage;
public class SpiImplTwo implements SpiInterface {
@Override
public void getName() {
System.out.println("SpiImplTwo");
}
}

资源文件:

 spipackage.SpiImpl
spipackage.SpiImplTwo

测试类:

 package spipackage;
import java.util.Iterator;
import java.util.ServiceLoader;
public class SpiTestClient {
public static void main(String[] args) {
ServiceLoader<SpiInterface> spiInterfaces = ServiceLoader.load(SpiInterface.class);
// 循环调用实现类中的方法
spiInterfaces.forEach(SpiInterface::getName);
// 获取某个实现类进行调用
Iterator<SpiInterface> iterator = spiInterfaces.iterator();
while (iterator.hasNext()) {
SpiInterface next = iterator.next();
if (next instanceof SpiImplTwo) {
next.getName();
}
}
}
}

测试结果:

什么是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,此方法很重要,代码如下所示:

 private T createExtension(String name) {
// 1、先获取class类
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
// 2、通过反射创建实例,且存入缓存
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 3、注入依赖,类似spring的依赖注入
injectExtension(instance);
// 4、将扩展对象包进wrapper对象中
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}

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

小结:SPI的作用

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

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

Dubbo源码学习之-SPI介绍的更多相关文章

  1. Dubbo源码学习--服务是如何引用的

    ReferenceBean 跟服务引用一样,Dubbo的reference配置会被转成ReferenceBean类,ReferenceBean实现了InitializingBean接口,直接看afte ...

  2. Dubbo源码学习--注册中心分析

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 注册中心 关于注册中心,Dubbo提供了多个实现方式,有比较成熟的使用zookeeper 和 redis 的 ...

  3. Dubbo源码学习--服务是如何发布的

    相关文章: Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 ServiceBean ServiceBean 实现ApplicationListener接口监听Conte ...

  4. Dubbo源码学习--集群负载均衡算法的实现

    相关文章: Dubbo源码学习文章目录 前言 Dubbo 的定位是分布式服务框架,为了避免单点压力过大,服务的提供者通常部署多台,如何从服务提供者集群中选取一个进行调用, 就依赖Dubbo的负载均衡策 ...

  5. Dubbo源码学习文章目录

    目录 Dubbo源码学习--服务是如何发布的 Dubbo源码学习--服务是如何引用的 Dubbo源码学习--注册中心分析 Dubbo源码学习--集群负载均衡算法的实现

  6. Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

    Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题 相关文章: Dubbo源码学习文章目录 前言 主要是前一阵子换了工作,第一个任务就是解决目前团队在 Dubbo 停机时产生的问题 ...

  7. Dubbo源码学习(二)

    @Adaptive注解 在上一篇ExtensionLoader的博客中记录了,有两种扩展点,一种是普通的扩展实现,另一种就是自适应的扩展点,即@Adaptive注解的实现类. @Documented ...

  8. Dubbo源码学习--服务发布(ServiceBean、ServiceConfig)

    前面讲过Dubbo SPI拓展机制,通过ExtensionLoader实现可插拔加载拓展,本节将接着分析Dubbo的服务发布过程. 以源码中dubbo-demo模块作为切入口一步步走进Dubbo源码. ...

  9. Dubbo源码学习之-Adaptive自适应扩展

    前言 最近三周基本处于9-10-6与9-10-7之间,忙碌的节奏机会丢失了自己.除了之前干施工的那段经历,只看参加软件开发以来,前段时间是最繁忙的了.忙的原因,不是要完成的工作量大,而是各种环境问题, ...

随机推荐

  1. wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name

    原文:wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name 写了一个 Storyb ...

  2. uva 1436 - Counting heaps(算)

    题目链接:uva 1436 - Counting heaps 题目大意:给出一个树的形状,如今为这棵树标号,保证根节点的标号值比子节点的标号值大,问有多少种标号树. 解题思路:和村名排队的思路是一仅仅 ...

  3. POST请求——HttpWebRequest

    string url=""; string param=""; string result = string.Empty; HttpWebRequest req ...

  4. 开源数据源使用 DBCP 和 C3PO

    jar包: commons-dbcp-1.4.jar commons-pool-1.5.6.jar mysql-connector-java-5.0.8-bin.jar 建立dbcp的配置文件 dbc ...

  5. VS2012中使用CEGUI项目发布到XP平台的问题(核心方法就一句话。“你项目使用的所有外部依赖库都用/MT编译。”)

    接着上一篇文章,详细说说如何把一个带CEGUI的项目发布到XP平台. 这个问题纠缠了我好几天.这里把详细解决思路记下来.有同样问题的朋友可以少走很多弯路. 核心方法就一句话.“你项目使用的所有外部依赖 ...

  6. Mysql数据库表的自增主键ID号乱了,需要重新排列。

    Mysql数据库表的自增主键ID号乱了,需要重新排列. 原理:删除原有的自增ID,重新建立新的自增ID. 1,删除原有主键:ALTER TABLE `table_name` DROP `id`; 2, ...

  7. 不要困在自己建造的盒子里——写给.NET程序员(附精彩评论)

    此文章的主旨是希望过于专注.NET程序员在做好工作.写好.NET程序的同时,能分拨出一点时间接触一下.NET之外的东西(例如10%-20%的时间),而不是鼓动大家什么都去学最后什么都学不精,更不是说. ...

  8. 如何计算memcache的容量

    在容量足够的情况下,当然是越大越好,但这样会造成浪费.不考虑这种情况.我们一般的情况是: memcache集群一开始创建会根据存储的数据量与访问量进行容量大小的估算.再算一个20%的冗余. 在网站快速 ...

  9. The specified type member 'IsLock' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

    var query = from C in objDb.GetDb<A>() join a in objDb.GetDb<B>().Where(m => m.Comput ...

  10. [机器学习]Generalized Linear Model

    最近一直在回顾linear regression model和logistic regression model,但对其中的一些问题都很疑惑不解,知道我看到广义线性模型即Generalized Lin ...