惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Cisco Blogs
爱范儿
爱范儿
有赞技术团队
有赞技术团队
博客园 - 【当耐特】
Jina AI
Jina AI
Project Zero
Project Zero
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
Simon Willison's Weblog
Simon Willison's Weblog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tenable Blog
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
Last Week in AI
Last Week in AI
月光博客
月光博客
雷峰网
雷峰网
G
Google Developers Blog
V
V2EX
T
Tor Project blog
罗磊的独立博客
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
W
WeLiveSecurity
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy International News Feed
S
Securelist
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
小众软件
小众软件
Scott Helme
Scott Helme
I
Intezer
T
Threat Research - Cisco Blogs
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
C
CERT Recently Published Vulnerability Notes
Security Archives - TechRepublic
Security Archives - TechRepublic
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LINUX DO - 最新话题
N
News | PayPal Newsroom
L
Lohrmann on Cybersecurity
T
Troy Hunt's Blog
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
人人都是产品经理
人人都是产品经理
Latest news
Latest news
AWS News Blog
AWS News Blog
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 郭慕荣

彻底搞懂 Codex Skill 与插件:区别、试用、核心作用、项目落地全指南(程序员实战向) Codex Skill 和插件到底有什么区别?程序员如何在项目中落地使用 claudecode 配置防止配置文件泄露总结 Claude code分析外部文件总结 Claude code三种模式详解 hasCompletedOnboarding详解 Claudecode 有哪些常用的命令 claude.md文件详解 claude code 的setting文件详解 Docker部署zookeeper总结 Mac中git ssh的配置(GitLab) Java 类加载机制 面试题(一) 如何监控和调优JVM性能? jvm常用的参数有哪些? 怎么配置? hashmap 和currenthashmap 的原理?详解一下 aop在项目中使用的场景?怎么使用? spring中常见的两种代理模式 Redis的zset 面试汇总 drools 规则引擎在线化配置 springcloud中常用的注解详解 springcloud中网关gateway总结 spring是怎么解决循环依赖的? MySQL 死锁 怎么处理? 在写left join的时候 是大表在左侧 还是小表在左侧(二) 在写left join的时候 是大表在左侧 还是小表在左侧(一) nacos客户端(接口调用者)如何感知被调用服务下线? (二) nacos客户端(接口调用者)如何感知被调用服务下线?(一) 在MySQL中 redolog undolog binlog 写入的场景,顺序
Java dubbo spring springboot中的spi机制
郭慕荣 · 2025-10-27 · via 博客园 - 郭慕荣

在 Java 生态中,SPI(Service Provider Interface) 是一种服务发现机制,允许框架或接口定义方通过配置文件指定接口的实现类,第三方可以通过实现接口并配置文件来扩展功能,实现 “接口与实现分离”。Dubbo、Spring、SpringBoot 均基于 SPI 思想设计了各自的扩展机制,但实现方式和应用场景有所不同。

在讲框架的 SPI 前,先了解 Java 原生 SPI,因为框架的 SPI 多基于其思想扩展:

  • 核心目的:允许服务提供者(第三方)为接口提供实现,接口调用方通过标准方式加载实现类。

  • 实现方式:

    1. 定义接口(如 com.example.MyService);
    2. 第三方提供实现类(如 com.example.impl.MyServiceImpl);
    3. 在 META-INF/services/ 目录下创建以接口全类名为文件名的文件(如 com.example.MyService),文件内容为实现类的全类名(com.example.impl.MyServiceImpl);
    4. 调用方通过 ServiceLoader.load(MyService.class) 加载所有实现类。
  • 缺点:

    • 一次性加载所有实现类,无法按需加载(浪费资源);
    • 无缓存机制,每次加载都重新实例化;
    • 不支持 IOC、AOP 等增强。

Dubbo 基于 Java 原生 SPI 做了增强,解决了原生 SPI 的缺陷,是 Dubbo 扩展机制的核心(如协议、注册中心、序列化等均通过 SPI 扩展)。

  • 配置文件位置:扩展配置文件放在 META-INF/dubbo/META-INF/dubbo/internal/META-INF/services/ 目录下(优先级依次降低)。
  • 配置文件格式:键值对形式(区别于 Java 原生的纯实现类),格式为 接口全类名 作为文件名,文件内容为 扩展键=实现类全类名(如 dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol)。
  • 核心类:ExtensionLoader,负责加载、缓存、管理扩展类,支持:
    • 按需加载:通过 “扩展键” 只加载指定实现类(而非全部);
    • IOC 依赖注入:自动注入扩展类的其他依赖(通过 setter 方法);
    • AOP 增强:支持对扩展类生成代理(如 Wrapper 包装类);
    • 自适应扩展:通过 @Adaptive 注解动态生成自适应实现类(根据运行时参数选择具体实现);
    • 激活扩展:通过 @Activate 注解指定条件(如 URL 参数、分组)激活扩展。

以扩展 Dubbo 的协议(Protocol 接口)为例:

  1. 定义扩展接口(若扩展已有接口,可跳过此步,直接实现 Dubbo 内置接口):

    // 需用@SPI注解标记为Dubbo SPI接口
    @SPI("myprotocol") 
  2. 实现接口:

    public class MyProtocol implements Protocol {
        @Override
        public String export() {
            return "自定义协议实现";
        }
    }
    
  3. 配置扩展文件:在项目 resources/META-INF/dubbo/ 目录下创建文件 org.apache.dubbo.rpc.Protocol(Dubbo 内置 Protocol 接口的全类名),内容为:

    myprotocol=com.example.MyProtocol  # 扩展键=实现类全类名
  4. 使用扩展:通过 ExtensionLoader 加载指定扩展:

    ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
    Protocol protocol = loader.getExtension("myprotocol"); 

Spring 的 SPI 机制称为 “Factories 机制”,核心用于框架内部的扩展点发现(如第三方组件集成 Spring 时,提供自定义的初始化器、监听器等)。

  • 核心目的:允许第三方通过配置文件向 Spring 注册接口实现类(如 ApplicationContextInitializerBeanFactoryPostProcessor 等)。
  • 配置文件位置:META-INF/spring.factories(固定路径和文件名)。
  • 配置文件格式:键值对形式,键为 “接口 / 类全类名”,值为 “实现类全类名列表”(用逗号分隔),例如:
    org.springframework.context.ApplicationContextInitializer=com.example.MyApplicationContextInitializer
    
  • 核心类:SpringFactoriesLoader,通过 loadFactories(Class<T> factoryType, ClassLoader classLoader) 方法加载指定类型的所有实现类。

以扩展 ApplicationContextInitializer(Spring 上下文初始化器)为例:

  1. 实现 Spring 扩展接口:

    public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            System.out.println("自定义初始化器:初始化Spring上下文");
        }
    }
    
  2. 配置 spring.factories:在项目 resources/META-INF/ 目录下创建 spring.factories 文件,内容为:

    org.springframework.context.ApplicationContextInitializer=com.example.MyApplicationContextInitializer
    
  3. 使用扩展:当 Spring 容器启动时,SpringFactoriesLoader 会自动加载 MyApplicationContextInitializer 并执行其 initialize 方法。

SpringBoot 的 SPI 机制是对 Spring Factories 的进一步强化,核心用于自动配置(AutoConfiguration) 和第三方 Starter 的扩展,是 SpringBoot “约定大于配置” 的核心实现。

  • 核心目的:通过配置文件自动注册 Bean、启用功能(如自动配置类、条件注解等)。
  • 配置文件位置:META-INF/spring.factories 或 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(SpringBoot 2.7 + 推荐后者,格式更简单)。
  • 核心扩展点:
    • org.springframework.boot.autoconfigure.EnableAutoConfiguration:指定自动配置类(最核心);
    • org.springframework.boot.env.EnvironmentPostProcessor:环境变量处理;
    • org.springframework.boot.diagnostics.FailureAnalyzer:启动错误分析等。
  • 加载逻辑:SpringBoot 启动时,通过 SpringFactoriesLoader 加载 EnableAutoConfiguration 对应的自动配置类,结合 @Conditional 条件注解(如 @ConditionalOnClass@ConditionalOnMissingBean)判断是否生效,最终向容器注册 Bean。

以自定义一个 “示例 Starter” 为例,实现自动配置 Bean:

  1. 创建自动配置类:

    @Configuration
    @ConditionalOnClass(MyService.class) 
  2. 配置自动配置类:

    • SpringBoot 2.7+:在 resources/META-INF/spring/ 目录下创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,内容为自动配置类全类名:
      com.example.MyAutoConfiguration
    • 旧版本:在 META-INF/spring.factories 中配置:
      org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.MyAutoConfiguration
      
  3. 使用扩展:其他项目引入该 Starter 后,SpringBoot 启动时会自动加载 MyAutoConfiguration,并在满足条件时注册 MyService 到容器,直接注入即可使用:

    @SpringBootApplication
    public class DemoApplication {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
            MyService service = context.getBean(MyService.class);
            System.out.println(service.hello()); 

扩展时需根据框架特性,遵循其配置规范和加载逻辑即可。