Dubbo源码通~SPI机制

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包,在需要时,用ServiceLoaderload方法加载对应的接口的实现类。

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("--------------");
    }
}
  • 输出结果
    Dubbo源码通~SPI机制_第1张图片

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("--------------");
}
  • 结果
    Dubbo源码通~SPI机制_第2张图片

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、服务方和客户方角度

  1. 服务方暴露自己的业务供客户方调用,则为提供API服务。
  2. 客户方实现服务方提供的接口,然后让服务方去调用自己,则为提供SPI服务。
    Dubbo源码通~SPI机制_第3张图片
    (别人博客的图)

作者自己的理解:

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

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

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

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

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

你可能感兴趣的:(Dubbo源码通)