从Spring Boot 4.0升到4.1,我在Maven和gRPC上栽了跟头

先说结论:Spring Boot 4.1从4.0升上去确实不难,官方也说"增量更新、无破坏性变更"。但我的项目升完之后,CI挂了、gRPC服务起不来了。折腾了一下午,问题都出在"小变更"上。 记录一下过程,给准备升级的人提个醒。

升级动机

我们组的主力服务跑在Spring Boot 4.0.6上,稳定了大半年。4.1发布那天(6月10号),我看了一眼changelog,gRPC自动配置、SSRF防护、延迟数据源连接------几个点都挺实用。尤其是gRPC,之前为了接微服务的RPC调用,我们自己写了一套starter来做自动配置,代码量不大但维护起来挺烦。4.1官方直接支持了,理论上可以把手写的starter干掉。 另外maven.test.skip那块改动我在release notes扫到了一眼,当时觉得"改个参数的事",没当回事。 就是这两个"没当回事",后面全炸了。

Maven跳测试失效,CI全线飘红

升完版本,本地mvn clean package跑了一下,编译通过,没问题。顺手推到远端,等CI。 十分钟后GitLab CI全红。 一开始以为是环境问题,登上去看日志,发现所有模块的测试都跑了------而且有些测试在CI环境里本来就是要跳过的(依赖外部数据库、需要特定网络配置之类的)。

yaml 复制代码
[ERROR] Tests run: 47, Failures: 3, Errors: 12, Skipped: 0
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:4.1.0:process-aot

我第一反应是:谁改了CI配置?看了一下pipeline定义,没动过。 再仔细看构建命令:

go 复制代码
mvn clean package -DskipTests

这个命令我们用了三年了,一直在用。4.0及以下版本,-DskipTests跳测试没问题。但4.1改了------Spring Boot Maven插件不再读-DskipTests了,得用maven.test.skip=true。 翻了一下release notes,确实有这么一条:

Maven plugin now requires maven.test.skip=true to skip tests during AOT processing; the -DskipTests parameter no longer takes effect.

注意这里有个细微区别:-DskipTests是跳过测试执行但还是编译测试代码 ,而-Dmaven.test.skip=true是连测试代码都不编译。4.1的AOT处理流程改了,-DskipTests这个参数被完全忽略了,不是"不跳",是"根本不认这个参数"。 改法很简单,CI脚本里把-DskipTests换成-Dmaven.test.skip=true

ini 复制代码
# 改前
mvn clean package -DskipTests

# 改后
mvn clean package -Dmaven.test.skip=true

改完CI就绿了。但我想说的是,如果你项目里有任何地方用了-DskipTests来跳测试------CI脚本、Makefile、Dockerfile构建步骤、甚至某个同事的alias------全得改。这不是一个"碰巧会踩到"的坑,是一个"一定会踩到但你发布notes里可能一眼扫过去"的坑。 我们组有个人的本地alias写的alias mvns='mvn package -DskipTests',升完之后他本地构建也挂了,跑来问我为什么。

gRPC自动配置跟我手写的starter打架

第二个坑更隐蔽。

前面说了,我们之前自己写了一个gRPC starter来做自动配置。4.1官方原生支持gRPC了,我想着正好把自建的干掉,省事。 删掉自建starter的依赖,加了spring-boot-starter-grpc,配了一下application.yml:

yaml 复制代码
spring:
  grpc:
    server:
      enabled: true
      port: 9090

启动,报错:

kotlin 复制代码
Caused by: java.lang.IllegalStateException: Multiple GrpcServerFactory beans found.
Cannot determine primary candidate. Consider marking one as @Primary.
  at org.springframework.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration.resolveFactory(GrpcServerAutoConfiguration.java:84)

大意是说找到了多个GrpcServerFactory的实现,不知道用哪个。 我愣了几秒------我明明把自建starter删了,哪来的多个? 全局搜了一下GrpcServerFactory,发现在另一个公共依赖包里(一个公司内部的通用组件库),有人也在里面写了一个@Configuration,注册了GrpcServerFactory的bean。这个包不是我们组的,是基础架构组维护的,好几个服务都在用。 问题就出在这:我删了本服务里的自建starter,但那个公共包里还有一份。4.1的gRPC自动配置检测到了两个工厂bean,就炸了。

解决方式有两个思路: 一是在公共包里给那个bean加上@ConditionalOnMissingBean,让它在有官方自动配置的时候不生效。但这是公共包,改动需要基础架构组评审发版,不是我能说了算的。 二是临时方案:在自己的配置里加一个@Primary标记,或者在application.yml里排除掉那个冲突的自动配置类:

yaml 复制代码
spring:
  autoconfigure:
    exclude:
      - com.company.common.grpc.CommonGrpcAutoConfiguration

我们选了方案二先顶着,等基础架构组下个版本修复。但这事给了我一个教训:升级之前不能只看自己的代码,得查一下所有依赖包有没有跟新特性冲突的配置。特别是那种"公司通用组件包",里面藏了什么你自己都不知道。 排查这类问题的方法是看启动日志里的CONDITIONS EVALUATION REPORT,Spring Boot启动时会打印所有自动配置的匹配和排除情况。加上这个JVM参数就能看到:

css 复制代码
java -jar app.jar --debug

日志里会有一段Positive matchesNegative matches,找gRPC相关的那几行,看哪些配置类被加载了,就能定位到冲突源。

其他值得一提的变更

除了上面两个实打实踩到的坑,还有几个变更我瞄了一眼,目前没影响到我们项目,但后面可能会用到:

SSRF防护 :新增了InetAddressFilter,可以给HTTP客户端配白名单/黑名单,阻止对指定地址段的出站请求。如果你的服务会对外部URL发起请求(比如爬虫、webhook回调),建议配上,不然一旦有参数注入类的漏洞,攻击者可以利用你的服务器去扫内网。

延迟数据源连接spring.datasource.connection-fetch=lazy,把物理连接的获取推迟到实际执行SQL的时候。我试了一下,启动速度确实快了一点(我们项目连接池配了20个连接,lazy模式下启动时不建连,能省个几百毫秒),但这个改动可能会影响依赖启动时连接状态的地方,比如有些健康检查或者初始化逻辑假设启动时连接池是满的。

@Async上下文传播 :现在@Async方法自动跨线程传播Micrometer的追踪上下文了,不用自己配。这个对我们组挺有用的,之前每次异步任务丢失traceId都要手动处理,现在省了。不过要注意一点,虚拟线程环境下ThreadLocal的行为跟平台线程不一样,如果你自定义了ThreadLocal存业务数据,升完还是要检查一下传播是否符合预期。

弃用项清理 :4.0里标记为deprecated的API在4.1里全部移除了,包括layertools JAR模式(改用tools模式)、Apache Derby支持、DevTools LiveReload。如果你的项目还在用这些,升之前得先处理掉。DevTools LiveReload被砍了没替代方案,官方也没解释为什么,社区有人猜是维护成本太高。

升级路径建议

如果你还在4.0上,升4.1的流程我觉得可以这么走:

先全局搜-DskipTests,全部换成-Dmaven.test.skip=true。这一步5分钟搞定但不改CI必挂。

然后搜一下项目里有没有手写的gRPC、Redis、JPA相关自动配置类。4.1新增了好几个自动配置,跟你自己写的可能冲突。有的话要么加@ConditionalOnMissingBean,要么用spring.autoconfigure.exclude排除。

最后在测试环境跑一遍全量回归,重点看启动日志里的CONDITIONS EVALUATION REPORT,确认没有意外的自动配置加载。

4.1整体来说是个不错的增量版本,gRPC自动配置和SSRF防护都很实用。但这些"小变更"藏得深,不看release notes根本不知道。

相关推荐
plainGeekDev2 小时前
Activity 间传值 → Navigation 参数
android·java·kotlin
plainGeekDev2 小时前
onActivityResult → ActivityResult API
android·java·kotlin
Sunia2 小时前
《AgentX 专栏》10-生产部署:3台2C4G云服务器把企业级Agent真正跑起来的完整方案
java·架构
江华森2 小时前
Django 6.0 从入门到实战教程(上机实操版)
后端
ZhengEnCi3 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
爱勇宝5 小时前
小红花成长新版:模板来了,鼓励也更容易开始
前端·后端·程序员
用户47949283569155 小时前
翻完 lark-cli 的 17 万行 Go 代码,我学到了什么
后端·openai
卷无止境6 小时前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
羑悻6 小时前
别再只接个 API 了!我用 EdgeOne Makers 手搓了一个“懂业务”的官网售前 AI
后端