如何为自己的库开发一个 Spring Boot Starter,并实现自动配置(Auto-Configuration)功能。
这属于 Spring Boot 高级开发技巧,通常用于:
- 开发公司内部共享组件
- 创建开源项目或商业 SDK
- 封装通用能力(如短信、支付、日志增强等)
我将用 通俗语言 + 结构化方式 帮你彻底理解这一节的核心思想和实践步骤。
🌟 一、核心目标:什么是"自定义 Starter"?
✅ 一句话解释:
你想让你的 Java 库(比如叫
acme-sdk
)能像 Spring Boot 的官方模块一样,只要引入一个依赖,就能自动生效、开箱即用。
例如:
xml
<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"。
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(AcmeClient.class) // 只有 AcmeClient 类存在时才加载
@ConditionalOnMissingBean // 用户没自己定义 AcmeClient 才创建
@EnableConfigurationProperties(AcmeProperties.class) // 启用配置绑定
public class AcmeAutoConfiguration {
@Bean
public AcmeClient acmeClient(AcmeProperties properties) {
return new AcmeClient(properties.getHost(), properties.getPort());
}
}
✅ 这个类会在满足条件时自动注册 AcmeClient
到 Spring 容器。
Step 2:定义配置属性(支持 application.yml)
为了让用户可以通过 application.yml
配置你的组件,你需要定义 @ConfigurationProperties
。
java
@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...
}
这样用户就可以写:
yaml
acme:
host: api.acme.com
port: 8080
login-timeout: 5s
check-location: true
💡 IDE 还会提示补全(因为生成了元数据文件)!
Step 3:注册 Auto-Configuration(关键!)
Spring Boot 不会自动发现你的配置类,必须通过 META-INF/spring.factories
文件显式声明。
文件路径:
src/main/resources/META-INF/spring.factories
内容:
properties
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 模块中):
xml
<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 排除方式:
xml
<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-starter
acme-spring-boot-autoconfigure
规则总结:
- 不要以
spring-boot
开头 - 使用你自己的命名空间(如
acme
) - starter 模块命名为
xxx-spring-boot-starter
- autoconfigure 模块命名为
xxx-spring-boot-autoconfigure
Step 7:编写测试(确保稳定性)
Spring Boot 提供了 ApplicationContextRunner
工具来测试自动配置行为。
示例测试代码:
java
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 {
@Bean
AcmeClient myAcmeClient() {
return new AcmeClient("custom-host", 9999);
}
}
✅ 这些测试可以验证:
- 自动配置是否正常工作
- 用户自定义 Bean 是否能覆盖
- 配置项是否生效
- 缺少依赖时是否会自动禁用
🧪 四、高级技巧补充
1. 控制加载顺序
有时候你的配置需要在其他配置之后执行(比如依赖 DataSource
)。
java
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class AcmeJdbcAutoConfiguration { ... }
或者指定优先级:
java
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
2. 支持 Reactive 或 Servlet 环境
java
@ConditionalOnWebApplication(type = Type.SERVLET)
public class AcmeServletAutoConfiguration { }
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class AcmeReactiveAutoConfiguration { }
3. 文档化配置项(IDE 友好)
确保每个字段都有 Javadoc,这样会自动生成 spring-configuration-metadata.json
,让 IDE 显示提示。
java
/**
* 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 模块包含:
SmsClientAutoConfiguration
SmsProperties
(accessKey, secret, signName...)
- starter 模块引入:
- autoconfigure
- 阿里云 SMS SDK
- commons-lang3 等常用工具
用户只需:
xml
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-sms-spring-boot-starter</artifactId>
</dependency>
yaml
aliyun:
sms:
access-key: xxx
secret: yyy
sign-name: 企鹅科技
然后注入 SmsClient
就能发短信了 ✅
🎯 最后提醒
建议 | 说明 |
---|---|
✅ 使用 @ConditionalOnMissingBean |
让用户能轻松覆盖默认行为 |
✅ 把第三方依赖设为 <optional>true</optional> |
避免强制引入不需要的库 |
✅ 不要用 @ComponentScan 扫描 autoconfigure 包 |
防止重复加载 |
✅ 提供清晰的文档和示例 | 帮助用户快速上手 |
如果你想,我可以为你生成一个 完整的自定义 Starter 示例项目模板(含 pom.xml、配置类、测试等),帮助你快速上手 😊