深入理解Spring Boot Starter及如何自定义Starter

发布于:2025-03-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

深入理解Spring Boot Starter及如何自定义Starter

Spring Boot Starter本质上是一种依赖管理和自动配置机制。通过引入一个Starter依赖,可以自动引入并配置一系列相关组件,极大地简化开发流程。比如使用spring-boot-starter-web,可以自动引入Spring MVC、Tomcat服务器、JSON解析器等,无需手动逐个配置。

Starter的自动配置原理

Spring Boot 会扫描所有 classpath 下 JAR 包中的 META-INF/spring.factories 文件,并按照其中的配置进行自动加载。

📌 详细解析

  1. 扫描机制

    • Spring Boot 使用 SpringFactoriesLoader 类来加载 spring.factories 文件中的配置。
    • 它会扫描 所有 依赖(包括 libs/dependencies 目录中的 JAR 包),寻找 META-INF/spring.factories,然后合并所有的配置项。
  2. 合并机制

    • 如果多个 JAR 包(如 spring-boot-starter-webspring-boot-starter-data-jpa 等)都定义了 spring.factories,Spring Boot 会把 所有 JAR 包中的配置合并,然后执行加载。
    • 示例:
      • spring-boot-autoconfigure.jarMETA-INF/spring.factories 可能包含:
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
        org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
        
      • 另一个 JAR 包 my-custom-starter.jar 可能包含:
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        com.example.custom.MyCustomAutoConfiguration
        
      • Spring Boot 会把这两部分合并,最终加载:
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
        org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
        com.example.custom.MyCustomAutoConfiguration
        
  3. 加载顺序

    • Spring Boot 不保证 spring.factories 配置的执行顺序,如果多个 JAR 包中存在相同的 Key(比如 EnableAutoConfiguration),它们都会被加载,但执行顺序不可预测
    • 如果需要控制加载顺序,可以使用 @AutoConfigureBefore@AutoConfigureAfter

META-INF/spring.factories 文件的作用

META-INF/spring.factoriesSpring Boot 的自动配置机制 之一,它的主要作用是:

  • 启用 Spring Boot 的自动配置(AutoConfiguration)
  • 自动注册 Spring 组件(如监听器、工厂类等)
  • 扩展 Spring Boot 功能

📌 spring.factories 作用范围

类型 作用
EnableAutoConfiguration 自动配置 Spring Boot 组件(最常见)
ApplicationContextInitializer Spring 上下文初始化时执行
ApplicationListener 监听 Spring 事件
FailureAnalyzer 处理异常分析,提供更友好的错误信息
EnvironmentPostProcessor 修改 Spring Environment,在启动时修改配置

📌 SpringFactoriesLoader 如何扫描?

Spring Boot 使用 SpringFactoriesLoader.loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) 方法来扫描所有 META-INF/spring.factories

public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
  • loadSpringFactories(classLoader) 方法会 遍历所有 JAR 包,加载 spring.factories
  • 所有 JAR 包的 spring.factories 文件的内容都会被合并并解析

📌 如何避免 spring.factories 冲突?

如果多个 JAR 包的 spring.factories 中包含 相同的 Key,但不同的实现(比如多个 JAR 里都有 EnableAutoConfiguration),可能会导致:

  • 重复加载(多个类都被注册)
  • 冲突(不同的类覆盖了同一个 Bean)
解决方案
  1. 使用 spring-boot-autoconfigure 提供的新方式

    • Spring Boot 2.7+ 推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    • 这样可以更好地控制哪些配置应该被加载,不会导致 spring.factories 的全局合并问题。
  2. 使用 @ConditionalOnMissingBean 避免重复加载

    • 在自动配置类中使用 @ConditionalOnMissingBean,避免同一个 Bean 被多次注册:
    @Configuration
    @ConditionalOnClass(MyService.class)
    public class MyAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public MyService myService() {
            return new MyService();
        }
    }
    
  3. 使用 @AutoConfigureBefore@AutoConfigureAfter

    • 控制自动配置类的加载顺序:
    @Configuration
    @AutoConfigureBefore(DataSourceAutoConfiguration.class)
    public class MyCustomAutoConfiguration {
        // 自定义配置
    }
    

📌 结论

Spring Boot 确实会扫描所有 JAR 包的 META-INF/spring.factories 文件,并合并所有配置项
这使得 Starter 组件可以自动注册需要的 Bean 和配置,但也可能导致 Bean 冲突或重复加载的问题
Spring Boot 2.7+ 推荐使用 AutoConfiguration.imports 作为更可控的替代方案


如何自定义一个Starter(以钉钉异常通知为例)

假设你想要实现当接口异常发生时自动发送钉钉通知的功能,创建一个Starter将是一个理想选择。

1. 创建新项目模块

例如:mycompany-starter-dingtalk

四、Starter实现步骤

  • 创建自动配置类:
@Configuration
@ConditionalOnClass(DingTalkNotifier.class)
@EnableConfigurationProperties(DingTalkProperties.class)
public class DingTalkAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public DingTalkNotifier dingTalkNotifier(DingTalkProperties properties) {
        return new DingTalkNotifier(properties.getWebhookUrl());
    }

    @Bean
    public GlobalExceptionHandler globalExceptionHandler(DingTalkNotifier dingTalkNotifier) {
        return new GlobalExceptionHandler(dingTalkNotifier);
    }
}
  • 定义属性类绑定用户配置
@ConfigurationProperties(prefix = "dingding")
public class DingTalkProperties {
    private String webhookUrl;

    public String getWebhookUrl() { return webhookUrl; }
    public void setWebhookUrl(String webhookUrl) { this.webhookUrl = webhookUrl; }
}

在用户的application.yml中配置:

dingding:
  webhook-url: "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"

注册自动配置

在项目的resources目录下创建META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.dingtalk.autoconfigure.DingTalkAutoConfiguration

Spring Boot启动时会自动扫描并加载这个配置。

五、Starter的命名规范建议

官方并没有强制性的命名约束,但 Spring Boot 社区和官方 Starter 一般遵循一些最佳实践,推荐使用 统一的命名规范,以提高可读性和易用性。

1️⃣ 官方 Starter 的命名规范

Spring Boot 官方 Starter 的命名规则通常是:

spring-boot-starter-<技术名称>
📌 典型官方 Starter 例子
Starter 说明
spring-boot-starter-web Web 开发 Starter,包含 Spring MVC、Tomcat 等
spring-boot-starter-data-jpa JPA 数据访问 Starter
spring-boot-starter-security Spring Security Starter
spring-boot-starter-thymeleaf Thymeleaf 模板引擎 Starter

👉 结论:

  • 所有官方 Starter 以 spring-boot-starter- 开头,然后跟 具体的技术名称
  • 这让开发者一眼就能知道 它是官方的,并且它支持什么技术

2️⃣ 自定义 Starter 的推荐命名规则

对于 自定义 Starter,社区推荐使用:

<公司/团队前缀>-starter-<功能名称>

或者:

<组织名称>-spring-boot-starter-<功能名称>
📌 推荐命名方式
命名示例 适用场景
mycompany-starter-dingtalk 企业内部使用,用于钉钉异常通知
com-example-spring-boot-starter-logging 社区 Starter,支持 Spring Boot 日志增强
custom-starter-redis 通用 Starter,适用于 Redis 相关增强功能
awesome-spring-boot-starter-cache 开源项目,提供缓存增强
📌 推荐命名规则

避免以 spring-boot-starter- 开头

  • 这样可以 避免和官方 Starter 混淆,让用户明确知道它是第三方 Starter
  • 推荐:mycompany-starter-xxxxxx-spring-boot-starter
  • 不推荐:spring-boot-starter-xxx ❌(可能导致误以为是官方的)

前缀代表团队或组织名称

  • 例如:
    • alibaba-starter-seata(阿里巴巴 Seata 事务管理)
    • baidu-starter-paddlepaddle(百度 PaddlePaddle AI 框架)
    • mycompany-starter-wechat(企业内部的微信消息推送)

后缀描述功能

  • starter-dingtalk(钉钉通知)
  • starter-mq(消息队列支持)
  • starter-log-enhancer(日志增强)

3️⃣ 约束和注意事项

🚀 官方约束

Spring Boot 不会 强制要求 Starter 必须符合某种命名规则,但官方有一些约定

  1. 官方 Starter 需要由 Spring 团队维护(放在 org.springframework.boot 下面)。
  2. 社区或第三方 Starter 不能以 spring-boot-starter- 开头,避免误导用户。
🚀 依赖命名

如果 Starter 需要提供 自动配置,通常需要提供:

  • 核心依赖(starter 依赖)
    <artifactId>mycompany-starter-dingtalk</artifactId>
    
  • 自动配置依赖(autoconfigure 依赖)
    <artifactId>mycompany-starter-dingtalk-autoconfigure</artifactId>
    

这样可以 拆分 Starter 的自动配置和核心功能,提高灵活性。

📌 结论

官方 Starterspring-boot-starter- 开头,后面跟具体技术,如 spring-boot-starter-web
自定义 Starter 推荐 mycompany-starter-xxxxxx-spring-boot-starter,避免与官方混淆。
组织前缀 可加 mycompany-com-example-,以区分不同的公司或团队。
最好拆分 starterstarter-autoconfigure,增强模块化和可扩展性

参考链接