1. 概述
Spring Boot 核心团队为大多数热门开源项目提供了 starter,但我们并不局限于此。我们也可以编写自己的自定义 starter。如果组织内部有需要在 Spring Boot 环境中使用的库,为它编写一个 starter 是很好的实践。
这些 starter 能帮助开发者避免冗长的配置,快速启动开发。但背后隐藏的机制有时让人困惑:为什么一个注解或仅在 pom.xml 中添加依赖就能启用这么多功能?
本文将揭开 Spring Boot 的神秘面纱,深入理解其幕后机制。然后运用这些知识,为自定义库创建一个 starter。
2. 揭秘 Spring Boot 自动配置
2.1. 自动配置类
从 2.7 版本开始,Spring Boot 启动时会从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中加载自动配置类。以 spring-boot-autoconfigure 项目为例,其文件内容如下:
...
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
...
可见,文件中每行都是一个自动配置类的全限定名,Spring Boot 会尝试加载这些类。根据上述代码片段,Spring Boot 将尝试加载 RabbitMQ、Cassandra、MongoDB 和 Hibernate 的所有配置类。
这些类是否实际执行取决于 classpath 中是否存在依赖类。例如,当检测到 MongoDB 相关类时,MongoAutoConfiguration
就会执行,并初始化所有 MongoDB 相关的 Bean。
这种条件初始化通过 @ConditionalOnClass
注解实现。来看 MongoAutoConfiguration
的代码片段:
@Configuration
@ConditionalOnClass(MongoClient.class)
@EnableConfigurationProperties(MongoProperties.class)
@ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDbFactory")
public class MongoAutoConfiguration {
// 配置代码
}
当 classpath 中存在 MongoClient
时,该配置类就会执行,用默认配置初始化一个 MongoClient
Bean 并注入 Spring 容器。
⚠️ 重要提醒:如果 Spring Boot 版本低于 2.7,它会从 META-INF/spring.factories
文件加载自动配置类。spring-boot-autoconfigure 项目中也有类似示例:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
此文件格式为单行逗号分隔列表,可使用反斜杠 \
换行提升可读性。
2.2. 通过 application.properties 自定义属性
Spring Boot 使用预配置的默认值初始化 Bean。要覆盖这些默认值,通常在 application.properties
中声明特定名称的属性。Spring Boot 容器会自动识别这些属性。
来看具体实现。在 MongoAutoConfiguration
代码片段中,@EnableConfigurationProperties
注解指定了 MongoProperties
类作为属性容器:
@ConfigurationProperties(prefix = "spring.data.mongodb")
public class MongoProperties {
private String host;
// 其他字段的标准 getter/setter
}
前缀 spring.data.mongodb
加上字段名构成 application.properties
中的属性名。例如设置 MongoDB 主机:
spring.data.mongodb.host = localhost
类似地,类中其他字段也可通过属性文件配置。
3. 创建自定义 Starter
基于第 2 节的概念,创建自定义 starter 需要以下组件:
- 为库编写自动配置类,以及用于自定义配置的属性类
- 创建 starter pom,引入库和自动配置模块的依赖
为演示,我们创建了一个简单的问候库,它接收不同时段的问候消息作为配置参数并输出。同时创建示例 Spring Boot 应用展示 starter 的用法。
3.1. 自动配置模块
我们将自动配置模块命名为 greeter-spring-boot-autoconfigure
。该模块包含两个核心类:
GreeterProperties
:通过application.properties
设置自定义属性GreeterAutoConfiguration
:为问候库创建 Bean
来看两个类的代码:
@ConfigurationProperties(prefix = "baeldung.greeter")
public class GreeterProperties {
private String userName;
private String morningMessage;
private String afternoonMessage;
private String eveningMessage;
private String nightMessage;
// 标准 getter/setter
}
@Configuration
@ConditionalOnClass(Greeter.class)
@EnableConfigurationProperties(GreeterProperties.class)
public class GreeterAutoConfiguration {
@Autowired
private GreeterProperties greeterProperties;
@Bean
@ConditionalOnMissingBean
public GreetingConfig greeterConfig() {
String userName = greeterProperties.getUserName() == null
? System.getProperty("user.name")
: greeterProperties.getUserName();
// 其他字段处理...
GreetingConfig greetingConfig = new GreetingConfig();
greetingConfig.put(USER_NAME, userName);
// ...
return greetingConfig;
}
@Bean
@ConditionalOnMissingBean
public Greeter greeter(GreetingConfig greetingConfig) {
return new Greeter(greetingConfig);
}
}
还需将 GreeterAutoConfiguration
的全限定名添加到 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
:
com.baeldung.greeter.autoconfigure.GreeterAutoConfiguration
✅ 兼容性提示:如果使用 Spring Boot 2.7 以下版本,需创建 src/main/resources/META-INF/spring.factories
文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baeldung.greeter.autoconfigure.GreeterAutoConfiguration
应用启动时,若 classpath 中存在 Greeter
类,GreeterAutoConfiguration
就会执行。成功执行后,它将通过 GreeterProperties
读取属性,向 Spring 应用上下文注入 GreetingConfig
和 Greeter
Bean。
@ConditionalOnMissingBean
注解确保这些 Bean 仅在不存在时才创建。这允许开发者通过在 @Configuration
类中自定义 Bean 来完全覆盖自动配置。
3.2. 创建 pom.xml
现在创建 starter pom,引入自动配置模块和问候库的依赖。
根据命名规范,所有非 Spring Boot 官方维护的 starter 应以库名开头,后缀 -spring-boot-starter
。因此我们将 starter 命名为 greeter-spring-boot-starter
:
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>greeter-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>greeter-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>greeter</artifactId>
<version>${greeter.version}</version>
</dependency>
</dependencies>
<properties>
<greeter.version>0.0.1-SNAPSHOT</greeter.version>
</properties>
</project>
3.3. 使用 Starter
创建示例应用 greeter-spring-boot-sample-app
,在 pom.xml 中添加 starter 依赖:
<dependency>
<groupId>com.baeldung</groupId>
<artifactId>greeter-spring-boot-starter</artifactId>
<version>${greeter-starter.version}</version>
</dependency>
Spring Boot 会自动完成所有配置,我们直接获得可注入使用的 Greeter
Bean。
通过 application.properties
覆盖 GreeterProperties
的默认值(使用 baeldung.greeter
前缀):
baeldung.greeter.userName=Baeldung
baeldung.greeter.afternoonMessage=Woha\ Afternoon
最后在应用中使用 Greeter
Bean:
@SpringBootApplication
public class GreeterSampleApplication implements CommandLineRunner {
@Autowired
private Greeter greeter;
public static void main(String[] args) {
SpringApplication.run(GreeterSampleApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
String message = greeter.greet();
System.out.println(message);
}
}
4. 总结
本教程重点介绍了如何创建自定义 Spring Boot starter,以及这些 starter 与自动配置机制如何协作,在幕后消除大量手动配置。掌握这些原理后,你也能轻松封装内部库,提升团队开发效率。