照着做一遍,你就能把 Spring Boot 3 的自动配置写对、把自己的 starter 发布出去,并给服务接上一套生产可用的监控。
Spring Boot 3 相比 2.x 有三个硬变化:最低 Java 17、底层 Spring Framework 6、所有 javax.* 换成 jakarta.*。这三点决定了下面所有代码的写法。
第一步:确认版本基线
pom.xml 里用 spring-boot-starter-parent 统一管版本,别手写 Spring 相关依赖的 version:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
</parent>
<properties>
<java.version>17</java.version>
</properties>
注意:如果是从 2.x 升上来,先把代码里所有 javax.servlet、javax.persistence、javax.validation 全量替换成 jakarta.*,否则编译能过、启动必炸。
第二步:把 starter 拆成两个模块
官方推荐的目录结构是两个 module:
xxx-spring-boot-autoconfigure:只放自动配置类和配置属性类,依赖 spring-boot-autoconfigure(optional 或 provided)
xxx-spring-boot-starter:空的 pom,只负责把 autoconfigure 模块 + 第三方依赖聚在一起
autoconfigure 模块里,第三方依赖要标 <optional>true</optional>,这样用户不引就不会被强制拖进来。
注意:只有你自己内部用的 starter 才命名为 spring-boot-starter-xxx 这种格式,那是 Spring 官方保留的命名空间。第三方一律用 xxx-spring-boot-starter。
第三步:写一个合格的自动配置类
@AutoConfiguration
@ConditionalOnClass(MyClient.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyClient myClient(MyProperties props) {
return MyClient.builder()
.endpoint(props.getEndpoint())
.timeout(props.getTimeout())
.build();
}
}
配置属性类用构造器绑定,别用 setter:
@ConfigurationProperties(prefix = "my.client")
public record MyProperties(String endpoint, Duration timeout) {
}
三个关键点:@AutoConfiguration 是 3.0 新增的专用注解(等价于 @Configuration(proxyBeanMethods = false) + 顺序控制能力);@ConditionalOnClass 放在类级别,避免类不存在时方法签名触发加载失败;@ConditionalOnMissingBean 让用户可以自己定义同类型 Bean 覆盖你的默认实现。
注意:@ConditionalOnMissingBean 只在自动配置类里可靠。如果你在普通 @Configuration 里用它,会因为用户 Bean 的注册顺序不确定而时灵时不灵。
第四步:注册自动配置(3.0 写法已变)
在 autoconfigure 模块的 src/main/resources/META-INF/spring/ 下新建文件:
org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容就是你的全限定类名,一行一个:
com.example.my.MyAutoConfiguration
注意:spring.factories 里注册 EnableAutoConfiguration 的写法在 2.7 已弃用、3.0 直接移除。同时 @AutoConfiguration 支持 after/before 属性来排顺序,别再依赖 @AutoConfigureAfter 猜顺序。
第五步:补上配置元数据
引入 spring-boot-configuration-processor(scope 为 provided),编译时会自动生成 spring-configuration-metadata.json,用户在 application.yml 里敲 my.client. 就有补全和文档提示。想给字段加中文说明,就在 META-INF/additional-spring-configuration-metadata.json 里补 description。
第六步:接上 Actuator 与 Micrometer
引入两个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
application.yml 里只暴露必要的端点:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: when-authorized
probes:
enabled: true
注意:千万别写 include: "*"。/actuator/env、/actuator/heapdump、/actuator/threaddump 在生产环境属于高危端点,会泄露配置和内存内容。
第七步:生产可用的监控三件套
健康探针:/actuator/health/liveness 给 K8s 判断要不要重启容器,/actuator/health/readiness 判断要不要接流量。自定义检查只需实现 HealthIndicator,返回 Health.up().withDetail("queue", size).build()。
业务指标:注入 MeterRegistry,用 Counter 计次数、Timer 计耗时:
Timer.builder("order.create")
.tag("channel", channel)
.register(registry)
.record(() -> orderService.create(req));
注意:标签(tag)的取值一定要收敛。把用户 ID、订单号当 tag 会造成指标基数爆炸,直接把 Prometheus 拖垮,这类维度应该放到日志里。
优雅停机:server.shutdown: graceful 配合 spring.lifecycle.timeout-per-shutdown-phase: 30s,避免发布时正在处理的请求被一刀切断。
第八步:把 traceId 打进日志
Spring Boot 3 用 micrometer-tracing 取代了 Sleuth。加 micrometer-tracing-bridge-brave(或 otel 桥),再改日志格式:
logging:
pattern:
level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"
这样一行日志就能串起整条调用链,排查线上问题不用再靠猜。
小结
- Spring Boot 3 的底线是 Java 17 +
jakarta.*,升级先搬包名。
- 自动配置类用
@AutoConfiguration + @ConditionalOnClass(类级)+ @ConditionalOnMissingBean。
- 注册入口换成
META-INF/spring/...AutoConfiguration.imports,spring.factories 已废弃。
- starter 拆成 autoconfigure + starter 两个模块,第三方依赖标
optional。
- Actuator 只暴露 health/info/metrics/prometheus,永远不要
include: "*"。
- 指标 tag 要收敛基数,traceId 进日志,停机开 graceful。