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接口
java 复制代码
package com.example.spidemo.javaspi;

/**
 * @author wdz
 * @date 2024/2/26
 */
public interface ApiService {
    public void test();
}
    1. 定义两个接口实现类
java 复制代码
package com.example.spidemo.javaspi;

/**
 * @author wdz
 * @date 2024/2/26
 */
public class OneApiService implements ApiService{
    @Override
    public void test() {
        System.out.println("OneApiService");
    }
}
java 复制代码
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. META-INF/services下创建全限定名的文件 文件中内容为:
bash 复制代码
com.example.spidemo.javaspi.OneApiService
com.example.spidemo.javaspi.TwoApiService
    1. 使用ServiceLoader调用实现类
java 复制代码
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);
    }
}

特点

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

优点:

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

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

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

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

缺点:

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

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

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

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

Spring SPI

示例

    1. 定义ApiService接口
java 复制代码
package com.example.spidemo.springspi;

/**
 * @author wdz
 * @date 2024/2/26
 */
public interface ApiService {
    public void test();
}
    1. 定义两个接口实现类
java 复制代码
package com.example.spidemo.springspi;

/**
 * @author wdz
 * @date 2024/2/26
 */
public class OneApiService implements ApiService{
    @Override
    public void test() {
        System.out.println("OneApiService");
    }
}
java 复制代码
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. META-INF下创建spring.factories 文件中内容为:
bash 复制代码
com.example.spidemo.springspi.ApiService=\
com.example.spidemo.springspi.OneApiService,\
com.example.spidemo.springspi.TwoApiService
    1. 使用SpringFactoriesLoader调用实现类
java 复制代码
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);
    }

}

特点

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
java 复制代码
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. 定义两个接口实现类
java 复制代码
package com.example.spidemo.dubbospi;

/**
 * @author wdz
 * @date 2024/2/26
 */
public class OneApiService implements ApiService{
    @Override
    public void test() {
        System.out.println("OneApiService");
    }
}
java 复制代码
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. META-INF/dubbospi目录下创建全限定名文件:

文件中内容为:

bash 复制代码
oneApiService=com.example.spidemo.dubbospi.OneApiService
twoApiService=com.example.spidemo.dubbospi.TwoApiService
    1. 使用ExtensionLoader调用实现类
java 复制代码
    @Test
    void dubboLoads(){
        ExtensionLoader<ApiService> extensionLoader = ExtensionLoader.getExtensionLoader(ApiService.class);
        ApiService oneApiService = extensionLoader.getExtension("oneApiService");
        oneApiService.test();
        ApiService twoApiService = extensionLoader.getExtension("twoApiService");
        twoApiService.test();
        
    }

特点

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

Dubbo SPI的特点具体如下:

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

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

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

Dubbo SPI的优点包括:

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

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

相关推荐
潘多编程20 分钟前
Spring Boot微服务架构设计与实战
spring boot·后端·微服务
2402_8575893625 分钟前
新闻推荐系统:Spring Boot框架详解
java·spring boot·后端
2401_8576226627 分钟前
新闻推荐系统:Spring Boot的可扩展性
java·spring boot·后端
Amagi.2 小时前
Spring中Bean的作用域
java·后端·spring
2402_857589362 小时前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso2 小时前
Rust 快速入门(一)
开发语言·后端·rust
sco52822 小时前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子3 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码3 小时前
详解JVM类加载机制
后端