一 概念与版本差异
- 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+)
步骤
- 新建模块(如 my-spi-starter ),无需启动类;添加依赖:
- spring-boot-autoconfigure
- (可选)spring-boot-configuration-processor(生成配置元数据)
- 定义业务接口与实现:
- 接口:com.example.spi.GreetingService
- 实现:com.example.spi.impl.EnglishGreetingService 、ChineseGreetingService
- 编写自动配置类:
- 路径:com.example.spi.config.GreetingAutoConfiguration
- 要点:使用 @Configuration 、条件注解(如 @ConditionalOnClass )、以及 @ConditionalOnMissingBean 保证用户可覆盖默认 Bean。
- 注册自动配置:在 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 写入自动配置类全限定名(每行一个)。
- 使用:在业务项目中引入 starter 依赖,按类型注入 GreetingService 即可使用(若用户自行定义了同类型 Bean,则以用户 Bean 为准)。
关键文件示例
-
AutoConfiguration.imports
javacom.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 增加:
javaorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.spi.config.GreetingAutoConfiguration -
条件注解(如 @ConditionalOnClass/@ConditionalOnMissingBean)是编写自动配置的最佳实践,可避免冲突并提升可覆盖性。
四 实战二 使用 Java SPI 插件机制
步骤
-
定义插件接口:com.example.spi.Plugin
-
多个实现:com.example.spi.impl.PluginA 、PluginB
-
注册插件:在 resources/META-INF/services/com.example.spi.Plugin 写入实现类全限定名(每行一个)。
-
加载插件(可在任意 Spring Bean 中):
javaimport 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、配置示例与可覆盖策略说明,便于团队接入与二次开发。