• SPI技术实现对比Java SPI、Spring SPI、Dubbo SPI


    概念

    SPI机制,全称为Service Provider Interface,是一种服务提供发现机制。

    SPI的核心思想是面向接口编程,它允许程序员定义接口,并由第三方实现这些接口。在运行时,SPI机制能够发现并加载所有可用的实现,然后根据需要实例化和使用它们。这种方式提供了一种灵活的方式来扩展应用程序或框架的功能,而不需要修改原有的代码。

    以下是SPI机制的一些关键点:

    • 服务提供者接口:SPI定义了一个接口,服务提供者需要实现这个接口以提供服务。例如,Java中的java.sql.Driver接口就是一个典型的SPI接口

    • 实现类注册:服务提供者需要提供一个实现类,并在类上使用特定的注解(如@ServiceProvider)来标记这个类是一个SPI实现。

    • 配置文件:在类路径下的指定目录中,服务提供者还需要创建一个以接口全名命名的文件,文件内容是实现类的全名,这样JVM就能在启动时找到并加载这些实现类。

    • 服务发现:当应用程序需要使用某个服务时,SPI机制会扫描指定目录下的配置文件,找到所有的实现类,并通过反射实例化它们,然后根据预设的规则选择一个实现来使用。

    • 应用场景:SPI机制常用于框架开发中,允许用户或第三方插件提供具体的实现,从而扩展框架的功能。例如,日志框架Slf4j就是通过SPI机制来绑定不同的日志实现。

    Java SPI

    示例

      1. 定义ApiService接口
    package com.example.spidemo.javaspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public interface ApiService {
        public void test();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
      1. 定义两个接口实现类
    package com.example.spidemo.javaspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class OneApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("OneApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package com.example.spidemo.javaspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class TwoApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("TwoApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
      1. META-INF/services下创建全限定名的文件
        在这里插入图片描述
        文件中内容为:
    com.example.spidemo.javaspi.OneApiService
    com.example.spidemo.javaspi.TwoApiService
    
    • 1
    • 2
      1. 使用ServiceLoader调用实现类
    package com.example.spidemo.javaspi;
    
    import java.util.ServiceLoader;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class ApiMain {
        public static void main(String[] args) {
            ServiceLoader<ApiService> services = ServiceLoader.load(ApiService.class);
            services.forEach(ApiService::test);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    特点

    Java SPI的特点包括服务发现机制、解耦和灵活性、易于扩展、延迟加载、易于替换组件以及内置于JDK。

    优点:

    • 面向接口编程:SPI机制允许开发者面向接口编程,而不是具体的实现,这样可以在不修改原有代码的情况下引入新的实现类。

    • 实现解耦:通过SPI机制,第三方服务模块的装配控制逻辑与调用者的业务代码分离,有助于降低模块间的耦合度。

    • 动态加载实现:应用程序可以根据实际业务需求动态地加载和使用不同的服务实现,提高了应用程序的灵活性。
      框架扩展:SPI机制特别适合用于框架开发,因为它允许用户或第三方为框架提供具体的实现,从而扩展框架的功能。

    • 提高启动速度:由于支持延迟加载,只有在真正需要使用某个服务时,相关的实现类才会被加载和实例化,这有助于提高应用程序的启动速度。

    缺点:

    • 不能按需加载:尽管ServiceLoader实现了延迟加载,但是它基本上只能通过遍历全部获取,这意味着不能精确地按需加载特定的实现。

    • 配置复杂性:使用SPI机制需要在META-INF/services目录下创建配置文件,并且需要手动添加实现类的全名,这增加了配置的复杂性。

    • 版本管理:当存在多个版本的同一服务实现时,可能会导致版本冲突的问题,管理起来较为困难。

    • 性能考虑:在大量实现类的情况下,SPI的遍历加载可能会导致性能问题。

    Spring SPI

    示例

      1. 定义ApiService接口
    package com.example.spidemo.springspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public interface ApiService {
        public void test();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
      1. 定义两个接口实现类
    package com.example.spidemo.springspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class OneApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("OneApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package com.example.spidemo.springspi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class TwoApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("TwoApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
      1. META-INF下创建spring.factories
        文件中内容为:
    com.example.spidemo.springspi.ApiService=\
    com.example.spidemo.springspi.OneApiService,\
    com.example.spidemo.springspi.TwoApiService
    
    • 1
    • 2
    • 3
      1. 使用SpringFactoriesLoader调用实现类
    package com.example.spidemo;
    
    import com.example.spidemo.springspi.ApiService;
    import org.junit.jupiter.api.Test;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.core.io.support.SpringFactoriesLoader;
    
    import java.util.List;
    
    @SpringBootTest
    class SpiDemoApplicationTests {
    
        @Test
        void contextLoads() {
    
            List<ApiService> apiServices = SpringFactoriesLoader.loadFactories(ApiService.class, Thread.currentThread().getContextClassLoader());
            apiServices.forEach(ApiService::test);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    特点

    Spring SPI机制非常类似,但还是有一些差异。

      1. Java SPI是一个服务提供接口对应一个配置文件,配置文件中存放当前接口的所有实现类,多个服务提供接口对应多个配置文件,所有配置都在services目录下。
      1. Spring SPI是一个spring.factories 配置文件存放多个接口及对应的实现类,以接口全限定名作为key,实现类作为value来配置,多个实现类用逗号隔开,'spring.factories’一个配置文件。
      1. 和Java SPI一样,Spring SPI也无法获取某个固定的实现,只能按顺序获取所有实现。

    Dubbo SPI

    示例

      1. 定义ApiService接口,接口需要使用@SPI
    package com.example.spidemo.dubbospi;
    
    import org.apache.dubbo.common.extension.ExtensionScope;
    import org.apache.dubbo.common.extension.SPI;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    @SPI(
            scope = ExtensionScope.MODULE
    )
    public interface ApiService {
        public void test();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
      1. 定义两个接口实现类
    package com.example.spidemo.dubbospi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class OneApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("OneApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package com.example.spidemo.dubbospi;
    
    /**
     * @author wdz
     * @date 2024/2/26
     */
    public class TwoApiService implements ApiService{
        @Override
        public void test() {
            System.out.println("TwoApiService");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
      1. META-INF/dubbospi目录下创建全限定名文件:
        在这里插入图片描述

    文件中内容为:

    oneApiService=com.example.spidemo.dubbospi.OneApiService
    twoApiService=com.example.spidemo.dubbospi.TwoApiService
    
    • 1
    • 2
      1. 使用ExtensionLoader调用实现类
    
        @Test
        void dubboLoads(){
            ExtensionLoader<ApiService> extensionLoader = ExtensionLoader.getExtensionLoader(ApiService.class);
            ApiService oneApiService = extensionLoader.getExtension("oneApiService");
            oneApiService.test();
            ApiService twoApiService = extensionLoader.getExtension("twoApiService");
            twoApiService.test();
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    特点

    Dubbo SPI的特点包括丰富的扩展点、灵活的配置以及与Dubbo框架的紧密集成,而其优点主要是易于扩展和维护,缺点则是学习成本相对较高且实现较为复杂。

    Dubbo SPI的特点具体如下:

    • 丰富的扩展点:Dubbo 提供了众多的扩展点,允许用户根据需求适配不同的实现,这使得Dubbo非常灵活,可以根据不同的场景进行定制和扩展。

    • 灵活的配置:用户可以通过配置文件轻松地增加新的接口实现,无需修改现有代码,这大大简化了维护工作并提高了系统的可维护性。

    • 与Dubbo框架紧密集成:Dubbo SPI 专为 Dubbo 框架设计,与框架的其他部分紧密集成,提供了更多的灵活性和高级扩展功能。

    Dubbo SPI的优点包括:

    • 易于扩展:开发者可以通过实现新的接口来扩展服务,而不需要修改原有的服务代码,这大大提高了系统的可维护性和可扩展性。

    • 维护简单:由于Dubbo SPI允许通过配置文件来添加新的服务实现,因此可以不停机更新服务实现,便于持续集成和快速迭代。

  • 相关阅读:
    【兔子王赠书第2期】《案例学Python(基础篇)》
    仙人掌之歌——大规模高速扩张(1)
    【机器学习】图神经网络:深度解析图神经网络的基本构成和原理以及关键技术
    22.cuBLAS开发指南中文版--cuBLAS中的Level-2函数spmv()
    C++语言程序设计任务书
    【无标题】
    wps表格按分隔符拆分单元格
    J2EE项目部署与发布(Linux版本)->jdk&tomcat安装,MySQL安装,后端接口部署,linux单体项目前端部署
    C语言编程题(四)有符号数与无符号数相加
    zabbix监控平台部署(二)
  • 原文地址:https://blog.csdn.net/Luck_gun/article/details/136292255