Dubbo源码通~SPI机制

    xiaoxiao2022-07-14  203

    SPI机制

    SPI概念:SPI全称为Service Provider Interface,是一种服务提供发现机制,将接口定义与实现解耦,提升程序的可扩展性。JDK的SPI本质:其本质是将接口实现类的全限定名配置在META-INF/services目录下的文件中中,由服务加载器ServiceLoader读取配置文件加载实现类,为在运行时,动态为接口加载实现类。Dubbo的SPI:Dubbo中并未使用JDK原生的SPI,而是借鉴SPI的思想,实现了自己的一套SPI机制。在原生的SPI特性基础上,优化并增加其他的特性,如增加对扩展点IoC和AOP的支持、不会一次性加载所有扩展点实现类。

    1、JDK-SPI

    实现:Jdk的SPI机制由ServiceLoader实现,开发者定义一个接口并提供实现类,同时在META-INF/services目录下创建一个以接口的全限定名为文件名的文件,并将其对应的实现类全限定名按行添加到文件中,最后打成一个jar包。使用:依赖上述打的jar包,在需要时,用ServiceLoader的load方法加载对应的接口的实现类。

    1.1、示例

    定义一个接口DemoService,有两个实现类: package com.leefox.Impl; //接口定义 public interface DemoService { String sayHello(String msg); } //实现类-ManServiceImpl public class ManServiceImpl implements DemoService { @Override public String sayHello(String msg) { return "man say: " + msg; } } //实现类-WomanServiceImpl public class WomanServiceImpl implements DemoService { @Override public String sayHello(String msg) { return "woman say: " + msg; } }

    在META-INF/services 文件夹下创建一个文件,名称为 DemoService 的全限定名 com.leefox.Impl.DemoService。文件内容为实现类的全限定的类名,如下:

    com.leefox.Impl.ManServiceImpl com.leefox.Impl.WomanServiceImpl 使用: @Test public void test() { ServiceLoader<DemoService> services = ServiceLoader.load(DemoService.class); for (DemoService demoService : services) { System.out.println(demoService.getClass().getName()); System.out.println(demoService.sayHello("hello spi")); System.out.println("--------------"); } } 输出结果

    2、DUBBO-SPI

    Dubbo 自己封装一套SPI加载机制,其逻辑被封装ExtensionLoader类中。ExtensionLoader加载器需要约定其SPI实现类的目录在META-INF/dubbo路径下。

    2.1、示例

    定义一个接口并加上1@SPI注解,有两个实现类。 //DubboSPIService接口定义 @SPI public interface DubboSPIService { String sayHello(String msg); } //实现类同Jdk SPI

    在META-INF/dubbo目录下增加一个接口全限定名的文件,并在文件中按照key-value的形式将实现类配置好。

    manSPI=com.leefox.dubbo.ManServiceImpl womanSPI=com.leefox.dubbo.WomanServiceImpl 使用 @Test public void DubboSPI_test() { //获取DubboSPIService对应的ExtensionLoader ExtensionLoader<DubboSPIService> extensionLoader = ExtensionLoader.getExtensionLoader(DubboSPIService.class); //通过key获取对应的实现类ManServiceImpl DubboSPIService manSPI = extensionLoader.getExtension("manSPI"); System.out.println(manSPI.sayHello("hello META-INF spi")); System.out.println("--------------"); //WomanServiceImpl DubboSPIService womanSPI = extensionLoader.getExtension("womanSPI"); System.out.println(womanSPI.sayHello("hello META-INF spi")); System.out.println("--------------"); } 结果

    3、SPI vs API

    3.1、定义

    SPI:Service Provider Interface SPI重点在:扩展实现功能,告诉你要遵从其定义规范 the SPI is the description of classes/interfaces/methods/... that you extend and implement to achieve a goal API:Application Programming Interface API重点在:调用使用功能,告诉你能获得什么功能? the API is the description of classes/interfaces/methods/... that you call and use to achieve a goal

    3.2、服务方和客户方角度

    服务方暴露自己的业务供客户方调用,则为提供API服务。客户方实现服务方提供的接口,然后让服务方去调用自己,则为提供SPI服务。 (别人博客的图)

    作者自己的理解:

    SPI可以理解为我定义了一个接口规范,实现方实现该接口来完成对应的功能,只要在接口定义的规范下,可以按照自己的需求来实现其想要的功能。然后给使用方使用。

    API可以理解为我定义了一个接口并实现了其功能,使用方拿来用就可以,但他能使用的功能只有我所实现的内容。

    4、面试怎么描述呢?(重点)

    首先SPI即Service Provider Interface,服务提供接口,比如你有个接口,改接口有 3 个实现类,那么在系统运行的时候到底选择哪个实现类呢?这就需要 spi 了,根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然后执行实现类的功能。jdk 的spidubbo采用了这种思想,但自己使用实现了一套spi机制,对应ExtensionLoader。相对于jdk 的spi来说,增加了一些特性,比如AOP,IoC,使用时才加载具体的实现类等等。比如Protocol,默认情况下是dubbo,如果我们需要改变,那么首先自己实现Protocol接口,并在调用时指定自定义的name,会拼接在URL上,在通过ExtensionLoader加载时,因为Protocol接口内部的方法上加上了@Adaptive,自适应扩展注解,会动态生成代理类,其中会解析传入的URL,得到我们自定义的实现类,并调用其方法。

    号外号外: 期待接下来结合在日常开发使用中dubbo的经历,学习看Dubbo这一个神级源码,抽象程度已出神入化~

    最新回复(0)