Spring Boot SPI 教程

​​​一 概念与版本差异​​

  • ​SPI(Service Provider Interface)​ 是一种在运行时发现并加载服务实现的机制,核心是"​接口与实现分离​ "。在 ​JDK​ 中通过 ​META-INF/services/接口全限定名​ 文件与 ​ServiceLoader​ 实现;在 ​Spring Boot​ 中扩展了该思想,使用 ​META-INF/spring.factories​ 与 ​SpringFactoriesLoader​ 来批量加载自动配置、监听器等扩展点。自 ​Spring Boot 2.7​ 起,官方为自动配置新增了 ​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​ 文件以替代 spring.factories 的自动配置注册方式,二者可并存(老项目仍可用 spring.factories)。Spring 的 SPI 加载机制与 IoC 容器深度集成,支持按条件加载与更灵活的实例化策略。

​​二 两种 SPI 用法速览​​

原生 Java SPI

  • 约定路径:​META-INF/services/接口全限定名​ ;内容为每行一个​实现类全限定名​。
  • 加载方式:​ServiceLoader.load(接口.class)​,迭代器按需实例化实现类(延迟加载、默认无参构造)。
  • 适用:与容器无关、轻量插件发现。

Spring Boot SPI

  • 约定路径与方式:
    • 自动配置:Spring Boot 2.7+ 使用 ​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​ (每行一个自动配置类);2.7 之前使用 ​META-INF/spring.factories​ 的 ​org.springframework.boot.autoconfigure.EnableAutoConfiguration​ 键。
    • 其他扩展点:在 ​META-INF/spring.factories​ 中以键值对注册(如 ​ApplicationListener​、自定义扩展接口)。
  • 加载方式:​SpringFactoriesLoader.loadFactories/loadFactoryNames​ 读取并实例化,可与 Spring 条件注解、环境集成。

​​三 实战一 自定义 Starter 并启用自动配置(Spring Boot 2.7+)​​

步骤

  1. 新建模块(如 ​my-spi-starter​ ),无需启动类;添加依赖:
    • ​spring-boot-autoconfigure​
    • (可选)​spring-boot-configuration-processor​(生成配置元数据)
  2. 定义业务接口与实现:
    • 接口:​com.example.spi.GreetingService​
    • 实现:​com.example.spi.impl.EnglishGreetingService​ 、​ChineseGreetingService​
  3. 编写自动配置类:
    • 路径:​com.example.spi.config.GreetingAutoConfiguration​
    • 要点:使用 ​@Configuration​ 、条件注解(如 ​@ConditionalOnClass​ )、以及 ​@ConditionalOnMissingBean​ 保证用户可覆盖默认 Bean。
  4. 注册自动配置:在 ​resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​ 写入自动配置类全限定名(每行一个)。
  5. 使用:在业务项目中引入 starter 依赖,按类型注入 ​GreetingService​ 即可使用(若用户自行定义了同类型 Bean,则以用户 Bean 为准)。

关键文件示例

  • AutoConfiguration.imports

    java 复制代码
    com.example.spi.config.GreetingAutoConfiguration
  • GreetingAutoConfiguration.java

    java 复制代码
    @Configuration
    @ConditionalOnClass(GreetingService.class)
    public class GreetingAutoConfiguration {
    
        @Bean
        @ConditionalOnMissingBean
        public GreetingService greetingService() {
            // 可按条件选择默认实现,或返回抽象工厂
            return new EnglishGreetingService();
        }
    }

说明

  • 若需兼容 ​Spring Boot <2.7​ ,可在同模块 ​resources/META-INF/spring.factories​ 增加:

    java 复制代码
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.example.spi.config.GreetingAutoConfiguration
  • 条件注解(如 ​@ConditionalOnClass/@ConditionalOnMissingBean​)是编写自动配置的最佳实践,可避免冲突并提升可覆盖性。

​​四 实战二 使用 Java SPI 插件机制​​

步骤

  1. 定义插件接口:​com.example.spi.Plugin​

  2. 多个实现:​com.example.spi.impl.PluginA​ 、​PluginB​

  3. 注册插件:在 ​resources/META-INF/services/com.example.spi.Plugin​ 写入实现类全限定名(每行一个)。

  4. 加载插件(可在任意 Spring Bean 中):

    java 复制代码
    import java.util.ServiceLoader;
    
    ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class);
    for (Plugin p : loader) {
        p.execute();
    }

适用场景

  • 与 Spring 容器解耦的纯发现机制;若需要依赖注入、AOP、条件控制,优先考虑 Spring Boot 的 ​SpringFactoriesLoader​ 或在自动配置中封装 ServiceLoader。

​​五 常见问题与最佳实践​​

版本与路径

  • ​Spring Boot 2.7+​ 推荐将自动配置写入 ​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​ ;老项目或第三方库仍可能使用 ​META-INF/spring.factories​,注意维护兼容性。

条件装配

  • 在自动配置类上使用 ​@ConditionalOnClass/@ConditionalOnMissingBean​ 等,明确生效条件与可覆盖性,避免与用户配置冲突。

避免类路径冲突

  • 明确依赖版本与导出包,防止不同 JAR 提供同名实现导致不可预期行为。

扩展点选择

  • 启动生命周期、环境处理等框架扩展点可用 ​spring.factories​ 注册(如 ​SpringApplicationRunListener​ 、​EnvironmentPostProcessor​ );业务插件发现可用 ​Java SPI​ 或 ​SpringFactoriesLoader​。

可维护性与文档

  • 为自定义 ​Starter​ 提供清晰的 ​README​、配置示例与可覆盖策略说明,便于团队接入与二次开发。
相关推荐
2601_962106645 小时前
【Java Set综合实战】商品管理系统 + 图书收藏系统|Product、Book自定义类全套代码
java
RuoyiOffice6 小时前
SpringBoot+Vue3 凭证生命周期设计:审核、过账、冲销与反过账怎样保持账目一致
java·spring boot·事务·springboot3·财务系统·会计凭证·ruoyi office
2601_962106646 小时前
HashMap入门|工号-姓名键值存储、两种遍历方式、根据key/value查
java
杨凯凡7 小时前
Spring Framework 7 API 版本管理实战:请求头路由、兼容迁移与废弃策略
java·rest api·springframework·spring boot 4·api版本管理
T_Wizard7 小时前
同一个问题写了 4 遍:官方 SDK / HTTP / Spring AI / LangChain4j,到底该选哪个
java·ai编程
hai_android7 小时前
AudioTrack 深度解析:从原理到实战
android·java·kotlin
斯维赤7 小时前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
Zan8 小时前
SQLite + SQLx 实战:为什么已经发布的数据库迁移文件,绝对不能再修改?
数据库·后端·面试
这个DBA有点耶9 小时前
数据同步的三个工程底线:DDL同步、断点续传、一致性校验——开源CDC的边界与KFS的路径
数据库·dba·代码规范
SelectDB10 小时前
列式存储为什么适合分析类负载?原理、限制与日志场景应用
数据库