美文网首页
dubbo源码(一)-SPI简单使用

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

作者: KissGoodby | 来源:发表于2019-02-12 17:24 被阅读0次

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的概念也基本有了了解。

相关文章

网友评论

      本文标题:dubbo源码(一)-SPI简单使用

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