手写一个最小 Starter:从 0 到能看懂
一、我们先定目标
我们做一个最简单的 starter,名字叫:ark-hello-starter
功能非常简单:
用户只要引入这个 starter,就能直接注入一个
HelloService来调用。
像这样:
@Autowired private HelloService helloService;然后:
System.out.println(helloService.sayHello());
输出:
hello from ark starter
二、你先理解:starter 到底拆成什么
最小 starter,核心就 3 样东西:
1)一个普通业务类
比如:HelloService
2)一个自动配置类
比如:HelloAutoConfiguration
3)一个配置注册文件
告诉 Spring Boot:
“启动时,把我的自动配置类也拿去加载”
三、整体结构先看懂
你可以先把 starter 理解成这个目录:
ark-hello-starter ├── src/main/java │ └── com/ark/starter │ ├── service │ │ └── HelloService.java │ └── autoconfigure │ └── HelloAutoConfiguration.java └── src/main/resources └── META-INF/spring └── org.springframework.boot.autoconfigure.AutoConfiguration.imports四、第一步:写一个最普通的服务类
这个类本身一点都不神秘,就是个普通 Java 类:
package com.ark.starter.service; public class HelloService { public String sayHello() { return "hello from ark starter"; } }五、第二步:写自动配置类
这一步才是 starter 的关键。
package com.ark.starter.autoconfigure; import com.ark.starter.service.HelloService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class HelloAutoConfiguration { @Bean public HelloService helloService() { return new HelloService(); } }你先别管复杂的条件注解。
先只看本质:
自动配置类 = 启动时帮你把 Bean 放进 Spring 容器
也就是:
以前你要手动写:
@Configuration public class MyConfig { @Bean public HelloService helloService() { return new HelloService(); } }现在 starter 帮你写好了。
六、第三步:注册自动配置类
这一步最重要,也是很多人第一次最陌生的地方。
在这个文件里写:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容:
com.ark.starter.autoconfigure.HelloAutoConfiguration
这一句的意思就是:
“Spring Boot 启动时,请把这个自动配置类也加载进来。”
七、到这里 starter 就成了
你现在回头看,其实 starter 没有你想象中那么玄学。
本质就是:
普通类
+ 自动配置类
+ 自动配置注册文件
八、再升级一步:加上 @ConditionalOnClass
现在我们把你前面学的东西塞进来。
比如改成这样:
package com.ark.starter.autoconfigure; import com.ark.starter.service.HelloService; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass(HelloService.class) public class HelloAutoConfiguration { @Bean public HelloService helloService() { return new HelloService(); } }虽然这个例子里HelloService.class本来就在 starter 里,条件判断意义不大,
但它是为了让你先看懂这个形式。
它表达的是:
只有类存在,配置才生效。
企业里常见的是判断第三方依赖,比如:
@ConditionalOnClass(RedisTemplate.class)
意思就是:
只有项目里引入了 Redis 相关类,我才自动配置 Redis 功能。
九、再升级一步:避免用户自己定义了同名 Bean
再加一个很常见的注解:
@ConditionalOnMissingBean
改成这样:
package com.ark.starter.autoconfigure; import com.ark.starter.service.HelloService; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass(HelloService.class) public class HelloAutoConfiguration { @Bean @ConditionalOnMissingBean public HelloService helloService() { return new HelloService(); } }这句话的意思:
如果用户自己没定义
HelloService,我才给他自动创建。
这个非常重要,因为 starter 不能太霸道。
十、现在你彻底理解一下运行流程
用户项目一启动,Spring Boot 干了这些事:
1. 启动应用
2. 读取 AutoConfiguration.imports
3. 发现 HelloAutoConfiguration
4. 判断条件是否满足
5. 满足则执行 @Bean
6. 把 HelloService 放入容器
7. 用户就能直接 @Autowired
这就是 starter 的本质流程。
十一、用户项目怎么用这个 starter
假设你已经把ark-hello-starter打成 jar,并安装到本地仓库。
用户项目里只要加依赖:
<dependency> <groupId>com.ark</groupId> <artifactId>ark-hello-starter</artifactId> <version>1.0.0</version> </dependency>然后直接写:
package com.demo.controller; import com.ark.starter.service.HelloService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { private final HelloService helloService; public TestController(HelloService helloService) { this.helloService = helloService; } @GetMapping("/hello") public String hello() { return helloService.sayHello(); } }访问:GET /hello
返回:hello from ark starter
十二、你现在会发现一件事
starter 其实不是“某种神秘技术”,它只是:
把别人本来要手动写的配置,提前封装好了。
所以它的价值是:
- 降低接入成本
- 统一技术方案
- 自动完成初始化
- 做成可插拔能力
十三、和 Android 经验怎么对齐
这块其实很好理解。
你可以把 starter 类比成:
Android 里的 SDK 接入包
比如你引一个网络库 SDK:
implementation "xxx-sdk"
然后它可能自动帮你:
- 提供 API Client
- 提供拦截器
- 提供初始化逻辑
- 提供默认配置
Spring Boot starter 也是这个意思,只不过它用的是:
- 依赖
- 自动配置
- Spring Bean 容器
所以你可以把它理解成:
后端里的“自动初始化 SDK 包”
十四、为什么 starter 往往要拆成两部分
你以后会看到一些文章说:
xxx-spring-boot-starterxxx-spring-boot-autoconfigure
这是因为企业里经常把它拆开。
1)starter
负责引依赖
2)autoconfigure
负责自动配置
你现在先不用纠结这个拆分。
你现阶段只要记住:
starter 核心一定绕不开自动配置类。
十五、给你一个最小可跑版本
我直接给你一套最简代码骨架,你以后回头抄都行。
1)HelloService.java
package com.ark.starter.service; public class HelloService { public String sayHello() { return "hello from ark starter"; } }2)HelloAutoConfiguration.java
package com.ark.starter.autoconfigure; import com.ark.starter.service.HelloService; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @ConditionalOnClass(HelloService.class) public class HelloAutoConfiguration { @Bean @ConditionalOnMissingBean public HelloService helloService() { return new HelloService(); } }3)AutoConfiguration.imports
com.ark.starter.autoconfigure.HelloAutoConfiguration
路径必须是:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
十六、你现在最容易混乱的点,我直接帮你拆掉
误区1:starter 是不是特殊语法
不是。
它本质上还是:
- 普通类
- 配置类
- 注册文件
误区2:没有 @ConditionalOnClass 就不是 starter
不是。
没有条件注解也能是 starter。
只是企业里一般都会加条件,避免乱加载。
误区3:starter 一定很复杂
不是。
最小 starter 非常简单。
复杂的是企业级 starter,会加上:
- 配置参数绑定
- 条件判断
- 默认值
- 多实现切换
- 日志
- 扩展点
十七、该掌握到什么程度
你现在不用追求“会写企业级 starter”。
你只要先到这一步就够了:
第一层
知道 starter 是什么
第二层
知道 starter 由什么组成
第三层
知道自动配置类怎么把 Bean 放进去
第四层
知道@ConditionalOnClass和@ConditionalOnMissingBean为什么常出现
到这里,你就已经不是“starter 小白”了。
十八、最短记忆
我给你一句最短记忆法:
starter = 依赖入口 + 自动配置
再展开一点:
starter = 帮用户自动把某套功能接进 Spring 容器
十九、最后我帮你把这件事落到你当前阶段
现在不用急着真的去写一个完整 starter 发到项目里。
现在最重要的是:
先把这个骨架理解了,以后看到
spring-boot-starter-web、redis starter、mybatis starter,你脑子里就知道它们在干嘛。
这就已经值了。
下一篇:
《手写 Starter 进阶:@ConfigurationProperties 实战(支持 application.yml)》
这个一接上,你就会明白为什么很多 starter 可以这样用:
ark:
hello:
enabled: true
message: hello world
这一步就会更像真正企业里的 starter。
