文章目录

  • SPI
  • dubbo SPI扩展点 与 java原生SPI扩展点:
  • 示例展示
  • JavaSPI
  • Dubbo SPI


SPI

全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。
SPI 机制在第三方框架中也有所应用,比如Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。
Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。

dubbo SPI扩展点 与 java原生SPI扩展点:

Dubbo的扩展点的加载是从JDK标准的SPI扩展点发现机制加强而来的。
Dubbo改进了JDK标准的SPI的一下问题:
1. JDK标准的SPI会一次性实例化扩展点的所有实现,如果有的扩展实现初始化很耗时的话,而且这个耗时的加载还没用上的话,将会造成资源的浪费。
2. 如果扩展点加载失败,拿不到扩展点的名称。比如:JDK 标准的ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因会被吃掉,和 ruby对应不起来,当用户执行ruby 脚本时,会报不支持 ruby的错,但这个异常不是真正失败的原因。
3. 增加了对扩展点的IOCAOP的支持,一个扩展点可以直接setter注入其他扩展点。

注意:源码版本均为 dubbo-2.6.4

示例展示

JavaSPI

前面简单介绍了SPI机制的原理,下面演示Java SPI的使用方法。首先,我们定义一个接口,名称Robot

public interface Robot {
	void sayHello();
}

接下来定义两个实现类,分别为OptimusPrimeBumblebee

public class OptimusPrime implements Robot {
	@Override
	public void sayHello() {
		System.out.println("Hello, I am OptimusPrime.");
	}
}
public class Bumblebee implements Robot {
	@Override
	public void sayHello(){
		System.out.println("Hello, I amBumblebee .");
	}
}

接下来在META-INF/services文件下创建一个文件,名称为Robo的全限定名org.appache.spi.Robot,文件内容为实现类的全限定的类名,如下:

org.appche.spi.OptimusPrime
org.appche.spi.Bumblebee

做好所需要的准备工作,接下来编写测试代码

public class JavaSPITest {
	ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
	System.out.println("Java SPI");
	serviceLoader.forEach(Robot: :sayHello);
}

测试结果显示:

dubbo 网络连接模型_Java

Dubbo SPI

Dubbo 并未使用Java SPI,而是重新实现了一套功能更强的SPI机制。Dubbo SPI 的相关逻辑被封装在了ExtensionLoader类中,通过 ExtensionLoader 我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放在 META-INF/dubbo路径下,配置内容如下:

optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee

Java SPI实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。
另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。下面来演示 Dubbo SPI 的用法:

public class DubboSPITest {

    @Test
    public void sayHello() throws Exception {
        ExtensionLoader<Robot> extensionLoader =  ExtensionLoader.getExtensionLoader(Robot.class);
        Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
        optimusPrime.sayHello();
        Robot bumblebee = extensionLoader.getExtension("bumblebee");
        bumblebee.sayHello();
    }
}

测试结果

dubbo 网络连接模型_加载_02

Dubbo SPI 除了支持按需加载接口实现类,还增加了 IOC 和 AOP 等特性