打造专属Spring Boot Starter
如何为自己的库开发一个 Spring Boot Starter,并实现自动配置(Auto-Configuration)功能。
这属于 Spring Boot 高级开发技巧,通常用于:
- 开发公司内部共享组件
- 创建开源项目或商业 SDK
- 封装通用能力(如短信、支付、日志增强等)
我将用 通俗语言 + 结构化方式 帮你彻底理解这一节的核心思想和实践步骤。
🌟 一、核心目标:什么是“自定义 Starter”?
✅ 一句话解释:
你想让你的 Java 库(比如叫
acme-sdk)能像 Spring Boot 的官方模块一样,只要引入一个依赖,就能自动生效、开箱即用。
例如:
<dependency><groupId>com.acme</groupId><artifactId>acme-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
引入后:
- 自动配置好
AcmeClient - 支持
application.yml中通过acme.xxx配置参数 - 如果类路径有某个类才启用 → 智能判断
这就是所谓的 “Starter + Auto-Configuration” 机制。
🔧 二、整体结构:一个典型的 Starter 包含什么?
一个完整的 Spring Boot Starter 通常由两个模块组成:
| 模块 | 作用 |
|---|---|
acme-spring-boot-autoconfigure | 核心:包含自动配置逻辑、条件判断、@ConfigurationProperties 等 |
acme-spring-boot-starter | 外壳:只负责引入 autoconfigure 模块 + 实际 SDK 依赖 |
示例目录结构:
acme-spring-boot-starter/
├── pom.xml # 只引入 acme-autoconfigure 和 acme-sdk
└── src/└── main/└── resources/└── META-INF/└── maven/...acme-spring-boot-autoconfigure/
├── pom.xml # 依赖 spring-boot-autoconfigure, acme-sdk (optional)
├── src/
│ └── main/
│ └── java/
│ └── com/acme/autoconfigure/
│ ├── AcmeAutoConfiguration.java
│ └── AcmeProperties.java
│ └── resources/
│ └── META-INF/
│ ├── spring.factories
│ └── spring-configuration-metadata.json
📌 注意:这两个模块可以合并成一个,但如果功能复杂、可选特性多,建议拆开。
🛠️ 三、关键步骤详解
Step 1:写一个 Auto-Configuration 类
这是最核心的部分 —— 写一个带有 @Configuration 的类,告诉 Spring Boot “在什么条件下创建哪些 Bean”。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AcmeClient.class) // 只有 AcmeClient 类存在时才加载
@ConditionalOnMissingBean // 用户没自己定义 AcmeClient 才创建
@EnableConfigurationProperties(AcmeProperties.class) // 启用配置绑定
public class AcmeAutoConfiguration {@Beanpublic AcmeClient acmeClient(AcmeProperties properties) {return new AcmeClient(properties.getHost(), properties.getPort());}
}
✅ 这个类会在满足条件时自动注册 AcmeClient 到 Spring 容器。
Step 2:定义配置属性(支持 application.yml)
为了让用户可以通过 application.yml 配置你的组件,你需要定义 @ConfigurationProperties。
@ConfigurationProperties("acme")
public class AcmeProperties {/*** Whether to check the location of acme resources.*/private boolean checkLocation = true;/*** Timeout for establishing a connection to the acme server.*/private Duration loginTimeout = Duration.ofSeconds(3);// getters & setters...
}
这样用户就可以写:
acme:host: api.acme.comport: 8080login-timeout: 5scheck-location: true
💡 IDE 还会提示补全(因为生成了元数据文件)!
Step 3:注册 Auto-Configuration(关键!)
Spring Boot 不会自动发现你的配置类,必须通过 META-INF/spring.factories 文件显式声明。
文件路径:
src/main/resources/META-INF/spring.factories
内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.acme.autoconfigure.AcmeAutoConfiguration,\
com.acme.autoconfigure.AcmeWebAutoConfiguration
⚠️ 注意事项:
- 必须是这个 key:
EnableAutoConfiguration - 多个类用
\换行连接 - 该类不能被
@ComponentScan扫到(避免重复加载)
Step 4:添加条件注解(Condition Annotations)
为了让自动配置更智能,你可以使用各种 @ConditionalOnXXX 来控制是否生效。
| 注解 | 用途 |
|---|---|
@ConditionalOnClass(Xxx.class) | 当类路径中有 Xxx 类才启用 |
@ConditionalOnMissingBean | 当容器中没有该类型 Bean 才创建 |
@ConditionalOnProperty(name="acme.enabled", havingValue="true") | 当配置项开启时才启用 |
@ConditionalOnResource("classpath:acme-config.xml") | 存在某个资源文件才启用 |
@ConditionalOnWebApplication | 只在 Web 应用中启用 |
@ConditionalOnExpression("${flag} == true") | SpEL 表达式控制 |
🎯 组合使用这些注解,可以让你的 starter 更加健壮、灵活。
Step 5:优化启动性能(推荐)
Spring Boot 提供了一个注解处理器,可以在编译期收集所有条件信息,加快启动速度。
添加依赖(在 autoconfigure 模块中):
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure-processor</artifactId><optional>true</optional>
</dependency>
它会在编译后生成
META-INF/spring-autoconfigure-metadata.properties,帮助 Spring Boot 提前过滤不匹配的配置类。
📌 注意:如果你只是在应用内写配置类(不是打包发布),不要把这个 jar 打进 fat jar。
Maven 排除方式:
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure-processor</artifactId></exclude></excludes></configuration>
</plugin>
Step 6:命名规范(重要!)
❌ 错误命名:
spring-boot-starter-acme(官方保留)mycompany-acme-starter(缺少命名空间)
✅ 正确命名:
acme-spring-boot-starteracme-spring-boot-autoconfigure
规则总结:
- 不要以
spring-boot开头 - 使用你自己的命名空间(如
acme) - starter 模块命名为
xxx-spring-boot-starter - autoconfigure 模块命名为
xxx-spring-boot-autoconfigure
Step 7:编写测试(确保稳定性)
Spring Boot 提供了 ApplicationContextRunner 工具来测试自动配置行为。
示例测试代码:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(AcmeAutoConfiguration.class));@Test
void defaultServiceIsCreated() {contextRunner.run(context -> {assertThat(context).hasSingleBean(AcmeClient.class);});
}@Test
void customUserServiceOverridesAutoConfig() {contextRunner.withUserConfiguration(UserConfig.class).run(context -> {assertThat(context).getBean("myAcmeClient").isSameAs(context.getBean(AcmeClient.class));});
}@Test
void whenPropertySet_thenUsesCustomHost() {contextRunner.withPropertyValues("acme.host=testhost").run(context -> {AcmeClient client = context.getBean(AcmeClient.class);assertThat(client.getHost()).isEqualTo("testhost");});
}@Test
void whenAcmeClientNotPresent_thenAutoConfigDisabled() {contextRunner.withClassLoader(new FilteredClassLoader(AcmeClient.class)).run(context -> {assertThat(context).doesNotHaveBean(AcmeClient.class);});
}// 测试类
static class UserConfig {@BeanAcmeClient myAcmeClient() {return new AcmeClient("custom-host", 9999);}
}
✅ 这些测试可以验证:
- 自动配置是否正常工作
- 用户自定义 Bean 是否能覆盖
- 配置项是否生效
- 缺少依赖时是否会自动禁用
🧪 四、高级技巧补充
1. 控制加载顺序
有时候你的配置需要在其他配置之后执行(比如依赖 DataSource)。
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class AcmeJdbcAutoConfiguration { ... }
或者指定优先级:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
2. 支持 Reactive 或 Servlet 环境
@ConditionalOnWebApplication(type = Type.SERVLET)
public class AcmeServletAutoConfiguration { }@ConditionalOnWebApplication(type = Type.REACTIVE)
public class AcmeReactiveAutoConfiguration { }
3. 文档化配置项(IDE 友好)
确保每个字段都有 Javadoc,这样会自动生成 spring-configuration-metadata.json,让 IDE 显示提示。
/*** Comma-separated list of locations to scan for acme resources.*/
private List<String> locations = Arrays.asList("/default");/*** Enable debug mode for acme engine.*/
private boolean debug = false;
✅ 总结:开发自定义 Starter 的完整流程
| 步骤 | 操作 |
|---|---|
| 1️⃣ | 创建两个模块:xxx-spring-boot-autoconfigure 和 xxx-spring-boot-starter |
| 2️⃣ | 在 autoconfigure 模块中编写 @Configuration 类,使用 @ConditionalOnXXX 控制条件 |
| 3️⃣ | 定义 @ConfigurationProperties("xxx") 绑定配置项 |
| 4️⃣ | 在 META-INF/spring.factories 中注册自动配置类 |
| 5️⃣ | 添加 spring-boot-autoconfigure-processor 提升启动性能 |
| 6️⃣ | 编写单元测试,使用 ApplicationContextRunner 验证各种场景 |
| 7️⃣ | 发布到 Maven 私服或公共仓库 |
| 8️⃣ | 用户只需引入 starter,即可开箱即用 |
💡 实际应用场景举例
假设你要封装一个 阿里云短信发送 SDK:
- 模块名:
aliyun-sms-spring-boot-starter - autoconfigure 模块包含:
SmsClientAutoConfigurationSmsProperties(accessKey, secret, signName…)
- starter 模块引入:
- autoconfigure
- 阿里云 SMS SDK
- commons-lang3 等常用工具
用户只需:
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-sms-spring-boot-starter</artifactId>
</dependency>
aliyun:sms:access-key: xxxsecret: yyysign-name: 企鹅科技
然后注入 SmsClient 就能发短信了 ✅
🎯 最后提醒
| 建议 | 说明 |
|---|---|
✅ 使用 @ConditionalOnMissingBean | 让用户能轻松覆盖默认行为 |
✅ 把第三方依赖设为 <optional>true</optional> | 避免强制引入不需要的库 |
✅ 不要用 @ComponentScan 扫描 autoconfigure 包 | 防止重复加载 |
| ✅ 提供清晰的文档和示例 | 帮助用户快速上手 |
如果你想,我可以为你生成一个 完整的自定义 Starter 示例项目模板(含 pom.xml、配置类、测试等),帮助你快速上手 😊
