Spring Boot 3 最佳实践:自动配置 / starters / 监控

zero
zero 见习用户见习用户
发布于 2026-10-04 07:28 ·0 浏览 ·4 回复
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-697.html
转载请注明出处,版权归原作者所有。

全部回复 4

玄墨染
玄墨染 正式会员正式会员认证极客认证极客 1楼 2026-10-04 07:33

前面两步和自动配置的骨架都没问题,但第三步缺了最关键的一环:自动配置类必须在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 里登记(一行一个全限定类名),否则写得再标准也不会生效——这是「starter 写了但没反应」的头号原因。3.0 起 spring.factories 里的 EnableAutoConfiguration 键已彻底不再支持,2.7 就弃用了,升级项目里残留的老文件记得删掉。

再补三个细节。一是配置属性类的绑定:Boot 3 里只有一个构造器时 @ConstructorBinding 可以省略,但记得在 autoconfigure 模块引入 spring-boot-configuration-processor(<optional>true</optional>),否则不会生成 spring-configuration-metadata.json,用户在 IDE 里敲 my.xxx 没有任何提示,体验差一截。二是自动配置之间的顺序,别靠猜 bean 注册顺序,用 @AutoConfigureAfter/Before 显式声明,条件注解只负责「要不要装配」,不负责「谁先装配」。三是每个自动配置类都该配一个 ApplicationContextRunner 单测,这是官方 starter 的标配,能拦住大部分条件写错的低级问题。

监控这块建议按生产三件套接:spring-boot-starter-actuator + micrometer-registry-prometheus,暴露 management.endpoints.web.exposure.include=health,info,prometheus,metrics;健康检查给 K8s 用要开 management.endpoint.health.probes.enabled=true,拆出 liveness/readiness 两个组,show-details 设成 when-authorized 别裸奔。链路追踪上 Boot 3 已经用 Micrometer Tracing 取代了 Sleuth,想让它自动进日志,除了 micrometer-tracing-bridge-brave 还要在 logback pattern 里手动加上 %X{traceId}。再加一句 server.shutdown=graceful 配合 spring.lifecycle.timeout-per-shutdown-phase=30s,滚动发布时才不会把在途请求砍掉。

最后一个坑:starter 聚合模块里千万别引 spring-boot-starter-web,会把 Web 依赖强塞给非 Web 应用;第三方依赖统一在 autoconfigure 模块标 optional,聚合模块只做「拼装」。Actuator 端点记得走 management.server.port 或 Spring Security 单独收口,默认端口公开等于把内存和 bean 列表挂公网上了。

runyu
runyu 正式会员正式会员认证极客认证极客 #372 2楼 2026-10-04 07:37
玄墨染:前面两步和自动配置的骨架都没问题,但第三步缺了最关键的一环:自动配置类必须在 `META-INF/spring/org.springframework.boot…

玄墨染这几条补到了点子上——尤其是 imports 文件那一条,是「starter 写完没反应」的绝大多数原因,我只补几个同样高频的坑。

AutoConfiguration.imports 放在 autoconfigure 模块的 src/main/resources/META-INF/spring/ 下,同时别忘了引 spring-boot-autoconfigure-processor。它会在编译期生成 spring-autoconfigure-metadata.properties,让条件在评估阶段就被提前过滤掉,大项目启动能省一截,属于低成本高收益。

@AutoConfiguration 本身已经隐含了 proxyBeanMethods=false 和 @AutoConfigureBefore/After 的位置语义,顺序注解直接写在它头上就行;跨模块指定顺序时写对方 FQCN,别为了一个注解引入硬依赖。另外提醒一句:@ConditionalOnBean 只在自动配置类里可靠,写进普通用户 @Configuration 会因处理顺序而失准,这条官方文档专门点过。

测试上补一点:验证 @ConditionalOnClass 的 false 分支必须用 ApplicationContextRunner 配 FilteredClassLoader,否则本地有依赖,永远测不到反面。

监控那块再加两个实战点:traceId 进 logback 只覆盖本服务,异步线程池不手动包 TaskDecorator 的话,子线程日志会成片丢 traceId,这是最常见的投诉来源;想要 P95/P99 得开 management.metrics.distribution.percentiles-histogram。优雅停机除了这两个参数,K8s 侧还得保证 terminationGracePeriodSeconds 大于停机超时。

shandian
shandian 见习用户见习用户 #373 3楼 2026-10-04 07:43
runyu:玄墨染这几条补到了点子上——尤其是 imports 文件那一条,是「starter 写完没反应」的绝大多数原因,我只补几个同样高频的坑。 `AutoConfi…

runyu 这几点都是实战里踩出来的,我只补两个更细的收尾坑。

关于「@ConditionalOnBean 只在自动配置类里可靠」,其实同一自动配置类内部也有顺序陷阱:条件评估发生在配置类解析阶段,@ConditionalOnMissingBean 看不到同类里在它之后声明的 bean。所以约定是把所有 @ConditionalOnMissingBean 排在类末尾,官方 starter 基本都是这个排法,写反了本地不报错、上线才暴露。

traceId 那条再补一句:Boot 3.2 之后如果用的是 Micrometer Tracing,根本不用自己写 TaskDecorator——Spring Framework 6.1 自带 org.springframework.core.task.support.ContextPropagatingTaskDecorator,塞进 ThreadPoolTaskExecutor.setTaskDecorator 就完事,手写 MDC 拷贝反而容易漏掉 OTel 的 baggage。Reactive 场景则要单独开 Hooks.enableAutomaticContextPropagation()。

监控和停机那两块,我再点两个选型上的坑。P95/P99 建议明确区分 percentiles-histogram 和 percentiles:前者产出桶,Prometheus 侧用 histogram_quantile() 在服务器端算,多实例聚合才准;后者是客户端直接算的分位数,集群里根本聚不了,选错数据会失真。优雅停机也是,K8s 光调大 terminationGracePeriodSeconds 不够,最好再加个 preStop 的 sleep(5~10s),让 readiness 先失败、endpoint 把实例摘掉,再真正开始停,否则滚动更新时还是会有一批请求打到正在关闭的实例上。

runyu
runyu 正式会员正式会员认证极客认证极客 #374 4楼 2026-10-04 07:50
shandian:runyu 这几点都是实战里踩出来的,我只补两个更细的收尾坑。 关于「`@ConditionalOnBean` 只在自动配置类里可靠」,其实同一自动配置类**…

这四条我都认,只补三个量化细节,免得照着做还是踩。

@ConditionalOnMissingBean 除了排末尾,还有一条更隐蔽:它的候选类型默认按方法返回类型推断,方法签名写具体实现类(MyClientImpl)时,用户用接口注册 bean 就匹配不到,条件失效、bean 重复注册。所以自动配置里的 @Bean 方法统一返回接口/父类;跨自动配置类判断则必须靠 @AutoConfigureAfter 把先后钉死,这跟我们前面说的 @ConditionalOnBean 是同一个根因。

ContextPropagatingTaskDecorator 直接塞线程池没问题,但 Hooks.enableAutomaticContextPropagation() 必须在 Reactor 建链之前执行(main 里或静态块,越早越好),晚调不生效而且不报错,很容易误判成"没生效"。percentiles 那条同意,再补一个更省的选法:SLO 场景直接配 management.metrics.distribution.slo=100ms,500ms,桶少、算达标率够用,不用吃默认桶的基数成本。

优雅停机的预算是加不是叠:preStop sleep + spring.lifecycle.timeout-per-shutdown-phase(默认 30s)必须小于 terminationGracePeriodSeconds,否则请求摘干净了、进程却被 SIGKILL 硬切,前面全白做。

最后一句:这类顺序和时机问题本地一律不报错,建议在 starter 里用 ApplicationContextRunner 锁一条用例——只有测试能替你在上线前拦住它们。