dubbo源码(一)-SPI简单使用

SPI简介

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。

下面我们通过代码演示一下什么是SPI,首先使用Java原生的SPI

Java原生的SPI

  1. 首先我们先定义一个接口,取名为Robot
/**
 * 演示jdk spi的使用
 *
 * @author hui.wang
 * @since 31 January 2019
 */
public interface Robot {

    /**
     * 测试接口
     */
    void sayHello();
}
  1. 接着我们定义两个实现类,分别为BumblebeeOptimusPrime
/**
 * {@link Robot} impl
 *
 * @author hui.wang
 * @since 31 January 2019
 */
public class OptimusPrime implements Robot{

    @Override
    public void sayHello() {
        System.out.println("Hello, I am Optimus Prime.");
    }
}
/**
 * {@link Robot} impl
 *
 * @author hui.wang09
 * @since 31 January 2019
 */
public class Bumblebee implements Robot{

    @Override
    public void sayHello() {
        System.out.println("Hello, I am Bumblebee.");
    }
}
  1. 接着在META-INF/services文件夹下创建一个文件,名称为 Robot 的全限定名com.hui.wang.dubbo.learn.spi.Robot,文件的内容为实现类的全限定名
com.hui.wang.dubbo.learn.spi.Bumblebee
com.hui.wang.dubbo.learn.spi.OptimusPrime
  1. 现在就可以测试了,我这里没有写测试类,直接写了一个main方法
/**
 * <b>SPI简介</b>
 * SPI 全称为 Service Provider Interface,是一种服务发现机制。
 * SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类
 *
 * @author hui.wang09
 * @since 31 January 2019
 */
public class Main {

    public static void main(String[] args) {

        /**
         * ServiceLoader 会加载META-INF/services 接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类
         */
        ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);

        System.out.println("Java SPI");
        serviceLoader.forEach(Robot::sayHello);
    }
}

输出的结果为:

Java SPI
Hello, I am Bumblebee.
Hello, I am Optimus Prime.

到这里Java原生的SPI简单使用已经结束,想必大家已经知道什么是SPI了,这里需要说明的是 ServiceLoader 会加载META-INF/services 接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这就是整个SPI的原理。

Dubbo 的SPI从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。
Dubbo 改进了 JDK 标准的 SPI 的以下问题:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

接下来我们看看dubbo的SPI是怎么使用的,和Java的SPI差不多

dubbo SPI 简单使用

  1. 首先我们先定义一个接口,取名为Robot,这里需要加一个com.alibaba.dubbo.common.extension.SPI的注解
/**
 * 演示jdk spi的使用
 *
 * @author hui.wang
 * @since 31 January 2019
 */
@SPI
public interface Robot {

    /**
     * 测试接口
     */
    void sayHello();
}
  1. 接着我们定义两个实现类,分别为BumblebeeOptimusPrime,和上面代码一致,这里不再列出代码
  2. 接着在META-INF/dubbo文件夹下创建一个文件,名称为 Robot 的全限定名com.hui.wang.dubbo.learn.spi.Robot,内容如下:
optimusPrime = com.hui.wang.dubbo.learn.spi.Bumblebee
bumblebee = com.hui.wang.dubbo.learn.spi.OptimusPrime
  1. 现在开始测试,没有写UT测试,写了main方法
public class DubboSPITest {

    public static void main(String[] args) {
        ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);

        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();

        /**
         * dubbo SPI支持默认设计
         * 配置 {@link com.alibaba.dubbo.common.extension.SPI}的value属性即可
         */
        Robot bumblebee = extensionLoader.getDefaultExtension();
        bumblebee.sayHello();
    }
}

打印结果为

Hello, I am Bumblebee.
Hello, I am Optimus Prime.

dubbo spi aop简单使用

保持上面的代码不变。

  1. 编写一个Robot实现类,取名为RobotWrapper,具体代码如下:
/**
 * <b>关于 dubbo spi aop 的使用</b>
 *
 * @author hui.wang09
 * @since 22 February 2019
 */
public class RobotWrapper implements Robot{

    private Robot robot;

    public RobotWrapper(Robot robot) {
        this.robot = robot;
    }

    @Override
    public void sayHello() {
        System.out.println("========================");
        System.out.println("before");
        System.out.println("========================");
        robot.sayHello();
        System.out.println("========================");
        System.out.println("after");
        System.out.println("========================");
    }
}
  1. 接着编辑在META-INF/dubbo文件夹下,名为 Robot 的全限定名com.hui.wang.dubbo.learn.spi.Robot文件,内容如下:
bumblebee=com.hui.wang.dubbo.learn.jdkspi.Bumblebee
optimusPrime=com.hui.wang.dubbo.learn.jdkspi.OptimusPrime
robotWrapper=com.hui.wang.dubbo.learn.dubbospi.RobotWrapper

这里新增了robotWrapper=com.hui.wang.dubbo.learn.dubbospi.RobotWrapper配置

  1. 编写测试类,代码如下:
    /**
     * <b>演示dubbo spi aop</b>
     * Wrapper class:
     * robotWrapper=com.hui.wang.dubbo.learn.dubbospi.RobotWrapper
     *
     * optimusPrime:
     * optimusPrime=com.hui.wang.dubbo.learn.jdkspi.OptimusPrime
     */
    @Test
    public void testRobotWrapper() {
        ExtensionLoader<Robot> extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);

        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        System.out.println(optimusPrime.getClass());
    }

打印结果如下:

========================
before
========================
Hello, I am Optimus Prime.
========================
after
========================
class com.hui.wang.dubbo.learn.dubbospi.RobotWrapper

从打印结果可以看出dubbo spi aop的使用和执行过程,dubbo spi 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。Wrapper 类有些类似 AOP,即 Wrapper 代理了扩展点。

到这里,关于dubbo SPI和Java SPI的简单使用已经结束,关于SPI的概念也基本有了了解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容