一、前言
很多人学 Spring Boot 时,会用各种 starter:
spring-boot-starter-webspring-boot-starter-data-redisspring-boot-starter-aop
但用久了,容易产生一个误区:
觉得 starter 就是“一个依赖包”。
其实不是。
starter 真正的核心是:
引入依赖后,Spring Boot 能根据条件,自动把你需要的 Bean 装配进容器。
这篇文章,我们就从 0 手写一个最小可运行版 starter,做一个日志模块,并支持配置开关。
最终效果是:
业务项目只要引入依赖,并配置:
ark.logging.enabled: true
就能直接注入并使用:arkLogger.info("用户注册成功");
二、最终目标
我们要做两个模块:
ark-parent
├── ark-logging-spring-boot-starter
└── demo-app
其中:
ark-logging-spring-boot-starter:我们自己写的 starterdemo-app:业务项目,用来测试 starter 是否自动生效
三、整体思路
starter 的核心流程其实很简单:
1. 定义配置类
2. 定义功能类(日志组件)
3. 定义自动配置类
4. 通过 AutoConfiguration.imports 注册自动配置类
5. 业务项目引入 starter 后自动生效
再配合:
@ConditionalOnProperty:配置开关控制@ConditionalOnClass:类存在才生效@ConditionalOnMissingBean:允许业务方覆盖默认实现
这就是 starter 的基本骨架。官方文档也是按这个思路描述自动配置的。
四、父工程 pom.xml
先创建父工程ark-parent。
ark-parent/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ark</groupId> <artifactId>ark-parent</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>ark-logging-spring-boot-starter</module> <module>demo-app</module> </modules> <properties> <java.version>17</java.version> <spring-boot.version>4.0.5</spring-boot.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>五、starter 模块实现
1. starter 模块 pom.xml
ark-logging-spring-boot-starter/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.ark</groupId> <artifactId>ark-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>ark-logging-spring-boot-starter</artifactId> <name>ark-logging-spring-boot-starter</name> <dependencies> <!-- 自动配置基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> </dependency> <!-- 提供基础 starter 能力和日志能力 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- 生成配置提示元数据,可选 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> </project>2. 定义配置属性类
starter 一般都会提供配置项。
比如我们做一个:
ark.logging.enabled:是否启用ark.logging.prefix:日志前缀
ArkLoggingProperties.java
package com.ark.logging.starter.properties; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "ark.logging") public class ArkLoggingProperties { /** * 是否启用日志模块 */ private boolean enabled = false; /** * 日志前缀 */ private String prefix = "[ARK-LOG]"; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public String getPrefix() { return prefix; } public void setPrefix(String prefix) { this.prefix = prefix; } }3. 定义日志接口
先定义一个最简单的接口。
ArkLogger.java
package com.ark.logging.starter.core; public interface ArkLogger { void info(String message); void error(String message); }4. 定义默认实现类
DefaultArkLogger.java
package com.ark.logging.starter.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultArkLogger implements ArkLogger { private static final Logger log = LoggerFactory.getLogger(DefaultArkLogger.class); private final String prefix; public DefaultArkLogger(String prefix) { this.prefix = prefix; } @Override public void info(String message) { log.info("{} {}", prefix, message); } @Override public void error(String message) { log.error("{} {}", prefix, message); } }这里用的是slf4j日志接口,业务项目最终看到的效果就是:
[ARK-LOG] 用户注册成功
5. 定义自动配置类
这一步是 starter 的核心。
ArkLoggingAutoConfiguration.java
package com.ark.logging.starter.autoconfig; import com.ark.logging.starter.core.ArkLogger; import com.ark.logging.starter.core.DefaultArkLogger; import com.ark.logging.starter.properties.ArkLoggingProperties; import org.slf4j.Logger; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @AutoConfiguration @ConditionalOnClass(Logger.class) @EnableConfigurationProperties(ArkLoggingProperties.class) @ConditionalOnProperty(prefix = "ark.logging", name = "enabled", havingValue = "true") public class ArkLoggingAutoConfiguration { @Bean @ConditionalOnMissingBean public ArkLogger arkLogger(ArkLoggingProperties properties) { return new DefaultArkLogger(properties.getPrefix()); } }这几个注解要重点理解。
@AutoConfiguration
表示这是一个自动配置类。Spring Boot 会把它当作自动装配候选。
@ConditionalOnClass(Logger.class)
表示只有类路径中存在Logger.class时才生效。
这类条件注解是自动配置常见写法。
@EnableConfigurationProperties(ArkLoggingProperties.class)
把我们写的配置属性类注册进 Spring 容器,并绑定配置文件内容。
@ConditionalOnProperty(...)
表示只有配置了:
ark.logging.enabled: true
时,自动配置才生效。
官方文档和 API 都说明了:
@ConditionalOnProperty常用于按配置开关决定是否创建 Bean;如果没有显式设置
matchIfMissing=true,默认属性缺失时不会匹配。
@ConditionalOnMissingBean
表示如果业务项目自己已经定义了ArkLogger,那 starter 就不再重复创建默认 Bean。
这就是典型的“给默认实现,但允许用户覆盖”。
6. 注册自动配置类
在 starter 模块下创建文件:
src/main/resources/METAINF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容如下:
com.ark.logging.starter.autoconfig.ArkLoggingAutoConfiguration
这一行非常关键。
Spring Boot 会从这个文件中读取自动配置类列表。
这是当前官方推荐机制。
六、demo-app 测试模块实现
1. demo-app 模块 pom.xml
demo-app/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.ark</groupId> <artifactId>ark-parent</artifactId> <version>1.0.0-SNAPSHOT</version> </parent> <artifactId>demo-app</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入自定义 starter --> <dependency> <groupId>com.ark</groupId> <artifactId>ark-logging-spring-boot-starter</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>2. 启动类
DemoApplication.java
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }3. 测试 Controller
TestController.java
package com.example.demo.controller; import com.ark.logging.starter.core.ArkLogger; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { private final ArkLogger arkLogger; public TestController(ArkLogger arkLogger) { this.arkLogger = arkLogger; } @GetMapping("/test/log") public String testLog() { arkLogger.info("调用了 /test/log 接口"); return "ok"; } }4. application.yml 配置
demo-app/src/main/resources/application.yml
server: port: 8080 ark: logging: enabled: true prefix: "[ARK-DEMO]"七、运行和测试
启动demo-app,访问:http://localhost:8080/test/log
返回: ok
控制台打印类似:
[ARK-DEMO] 调用了 /test/log 接口
这就说明:
- starter 已经被引入
- 自动配置类已经被识别
- 条件匹配成功
ArkLoggerBean 已经自动注入成功
八、开关验证
现在把配置改成:
ark:
logging:
enabled: false
再次启动。
由于@ConditionalOnProperty不满足,ArkLoggerBean 不会创建。
这时TestController依赖注入会失败,项目启动也会报错。
这就说明:
你的 starter 开关确实生效了。
九、允许业务方覆盖默认实现
如果业务项目不想用 starter 默认日志实现,而想自定义,也可以。
比如在demo-app中自己写:
CustomLoggerConfig.java
package com.example.demo.config; import com.ark.logging.starter.core.ArkLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CustomLoggerConfig { @Bean public ArkLogger arkLogger() { return new ArkLogger() { private final Logger log = LoggerFactory.getLogger("CustomArkLogger"); @Override public void info(String message) { log.info("[CUSTOM] {}", message); } @Override public void error(String message) { log.error("[CUSTOM] {}", message); } }; } }由于 starter 里用了:
@ConditionalOnMissingBean
所以此时不会再创建默认的DefaultArkLogger。
业务项目会优先使用自己的实现。
这也是自动配置最经典的扩展方式之一。
十、starter 的底层本质
你要把这一套彻底理解透:
普通 jar 包
只是提供类和方法。
starter
不只是提供类,还提供:
- 配置项
- 自动配置类
- 条件装配逻辑
- 默认 Bean
- 扩展覆盖能力
也就是说:
starter 的本质,是把“Bean 创建逻辑”从业务项目里抽走,交给 Spring Boot 按条件自动完成。
十一、几个常见坑
1. 忘记写AutoConfiguration.imports
这是最常见的问题。
如果没写这个文件,自动配置类根本不会被 Spring Boot 识别。
2.@ConditionalOnProperty配置写错
比如你写了:
@ConditionalOnProperty(prefix = "ark.logging", name = "enabled", havingValue = "true")
那配置必须是:
ark.logging.enabled=true
或者 yml 形式:
ark: logging: enabled: true3. 不要在 starter 里乱用@ComponentScan
starter 最稳的方式,是:
- 自动配置类里手动
@Bean - 配合条件注解控制
不要搞一堆扫描,把不该装配的类也扫进去。
4.matchIfMissing
如果你想做到“默认开启”,可以这样写:
@ConditionalOnProperty( prefix = "ark.logging", name = "enabled", havingValue = "true", matchIfMissing = true )但默认情况下,matchIfMissing是false。
十二、面试怎么讲
你以后面试说这段就很像样:
我写过一个自定义 Spring Boot starter。
使用@AutoConfiguration定义自动配置类,并通过AutoConfiguration.imports注册。
在自动配置类中结合@ConditionalOnProperty做功能开关,结合@ConditionalOnMissingBean允许业务方覆盖默认实现。
最终业务项目只需要引入依赖并配置开关,就可以自动获得日志组件能力。
这就不是“只会用注解”了。
这是你已经开始理解 Spring Boot 自动装配机制了。
十三、总结
这篇 starter 实战,你要记住的其实就 4 句话:
1. starter 不是普通工具包,它是自动装配包
2. 自动配置类是 starter 的核心
3. 条件注解决定 Bean 是否生效
4. AutoConfiguration.imports 决定 Spring Boot 能不能找到你的自动配置类
再压缩成一句话就是:
starter = 依赖 + 自动配置 + 条件装配 + 默认实现