
Part 1:背景
Spring Boot 作为企业级应用开发的框架被广泛使用。随着技术的不断发展,Spring Boot 开源社区已经不再维护 Spring Boot 2.x 版本,新的功能和问题修复都在最新的 Spring Boot 3.x 版本进行更新。Spring Boot 3 提供了更好的性能优化、支持更高的 Java 版本,并且在持续更新和修复安全漏洞。因此,升级到 Spring Boot 3 是保持应用安全性、性能和兼容性的必要步骤。
eBay 作为一家领先的电子商务公司,一直在不断探索和应用新技术以满足不断增长的业务需求。在这个过程中,出于对安全性、新功能和高性能的考虑,eBay 基于 Spring Boot 的基础设施开发框架也需要从 Spring Boot 2 升级到 Spring Boot 3。目前公司内已经有超过数千个应用分布在不同的团队中,如何将这些不同类型、不同复杂度的应用平滑、自动化地升级到 Spring Boot 3,成为了一个亟待解决的问题。
Part 2:痛点与挑战
大量不兼容改动导致升级成本过高
Spring Boot 中包含大量的组件,例如 spring-security 和 spring-data-elasticsearch。随着从 Spring Boot 2 到 Spring Boot 3 的升级,这些组件中的大部分也有版本升级,并且很多升级是不向后兼容的。这就意味着在升级到 Spring Boot 3 时,使用到这些组件的代码都需要做对应的修改以适配这些不兼容改动。一个复杂的应用通常会组合几十甚至上百个组件一起使用,要全部升级和改造的难度可想而知。此外,有些组件升级后的版本变化非常大,业务开发者往往没有足够的知识来修改和适配这些巨大改动。
为了具体量化升级成本,我们做了如下分析:
- 我们扫描了 Spring Boot 3 的组件及其使用到的间接依赖库的代码,并且分析每个库在升级前和升级后的对比结果。分析发现:这些库总共有多达 80000+ 个不向后兼容的 API 改动。
- 将 eBay 的所有 Spring Boot 应用做一次升级预演,通过分析这些应用涉及到的问题,我们归纳出了 700+ 类升级导致的不兼容问题,例如:
- Spring-security 库中自定义 Web Filter 的方式发生了巨大变化,在 Spring Boot 2 时期被广泛使用的 WebSecurityConfigurerAdapter 类在 Spring Boot 3 已经被彻底删除。图 1 是一个简单的代码示例,说明了在 Spring Boot 2 和 Spring Boot 3 的框架下使用 Web Filter 的不同写法。
- Spring Boot 3 适配的 Elasticsearch 版本是 8.x,但 eBay 的 elasticsearch server 的版本是 7.x,因此无法兼容,使用 Elasticsearch client 的应用必须全部迁移到使用 Opensearch。

图1: Web Filter 在 Spring Boot 2 和 Spring Boot 3 的不同写法
这些问题都是阻碍我们推进 Spring Boot 3 升级的难题,业务开发者很难自行完成应用的升级,我们必须从框架维护者的角度提供自动化升级和验证的流程。
除了应用,还有依赖库需要升级
除了以微服务形式开发和部署的应用之外,eBay 还存在大量的依赖库。这些依赖库可能来自开源社区,也可能是内部自行编写的,它们通常带有业务属性并且逻辑复杂。例如,一个用于连接 MySQL 数据库的依赖库,可能被多个使用到 MySQL 的应用所复用。
和应用一样,这些依赖库也需要进行 Spring Boot 2 到 Spring Boot 3 的升级。但相比应用,依赖库有一些更复杂的难题:
- 依赖库的种类繁多。如图 2 所示,在 eBay,一个应用可能使用多种不同来源的依赖库。
- 依赖库的数量庞大。以内部业务依赖库为例,其总数在 10000+ 以上,而其中与 Spring Boot 3 不兼容的数量也达到了 5000+;而开源社区依赖库的数量甚至更多。
- 我们通过分析发现,在与 Spring Boot 3 不兼容的依赖库中,大部分依赖库只需要做 JavaEE -> JakartaEE 的升级即可实现对 Spring Boot 3 的兼容。换言之,这些依赖库并没有使用复杂的且在 Spring Boot 3 被移除的方法,只是因为 JavaEE -> JakartaEE 的变化才导致其无法直接在 Spring Boot 3 环境中使用。
- 应用和依赖库之间的依赖关系、版本管理错综复杂。如果依赖的库没有升级,那么应用就无法完成升级;而如果依赖的库完成了升级从而产生了一个新版本,应用的开发者又该如何知道这个新版本的存在呢?

图2: 应用和多种依赖库
Part 3:解决方案
基于上述痛点,我们希望设计一种能够将应用和依赖库无感升级到 Spring Boot 3 的自动化方案。
架构设计

图3: 自动化方案 - 整体架构
如图 3 所示,整体架构由下至上可以分为三部分:
后端服务层(Backend Layer)
为 Spring Boot 3 自动化升级的核心层,也是本文要重点介绍的部分,主要包含三个组件:
- Code Rewrite Flow:将基于 Spring Boot 2 的用户代码升级到 Spring Boot 3,基于 OpenRewrite 和 GenAI。
- Library Management Service:用于管理依赖库的 Spring Boot 3 兼容版本。提供 UI 交互面向依赖库的开发者,开发者可以将其负责的依赖库的兼容 Spring Boot 3 的新版本注册到该 Service,从而可以在应用升级时被自动使用到该新版本。
- Jakarta Converter:对于有 Java EE 问题的依赖库,使用该工具可以自动地转换出一个新的 Jakarta EE 版本的依赖库,并且自动注册到 Library Management Service 以供应用升级时自动使用。
中间服务层(Service Layer)
主要作用是为用户提供自动化升级的能力:
- 对于应用的开发者:我们提供自动的升级服务。将 eBay 的所有应用分批次按不同的月份做逐步升级,对每个应用在经过了实现的升级预演和测试之后,在需要升级的时间点,自动调用 Backend 层的服务将用户的代码生成一份 Spring Boot 3 兼容的版本,并且为用户生成一个合并请求(Pull Request)。
- 对于依赖库的开发者:我们同样提供自动化升级的能力。首先我们会扫描公司内的应用实际使用到了哪些依赖库的哪些版本,对于这些依赖库中具有 Java EE 问题的依赖库,优先使用 Backend 层的 Jakarta Converter 工具来自动转换出一个 Spring Boot 3 兼容的版本;对于其他有复杂不兼容问题的依赖库,和应用类似,我们会调用 Code Rewrite Flow 去生成一份 Spring Boot 3 兼容的代码,并且为用户生成一个合并请求(Pull Request)。
用户层(User Layer)
对于应用和依赖库的开发者,只需要简单几步就能完成 Spring Boot 3 升级:
- 对于应用的开发者:在收到 Service 层发来的合并请求后,只需要像日常的开发一样对该合并请求做代码评审、合并以及后续的发布上线,即可完成 Spring Boot 3 的升级流程。该流程完全自动化且无损。
- 对于依赖库的开发者:同样的,在收到 Service 层发来的合并请求后,只需要像日常的开发一样对该合并请求做代码评审、合并以及后续的新版本部署,之后还需要把新版本通过 UI 注册到 Library Management Service 即可完成全部的升级流程。
如上所述,不论是应用还是依赖库,在我们的自动化升级流程的帮助下,Spring Boot 3 的升级是完全自动化且无损、平滑的。开发者只需像往常一样做代码的评审和上线即可,复杂的代码改动和依赖管理都被我们的解决方案自动完成,开发者无需关注。
下面我们重点介绍 Backend 层中的重要组成部分。
Code Rewrite Flow: 代码升级利器
在 Spring Boot 2 -> Spring Boot 3 的升级中,最难的部分就是如何处理代码中大量的不兼容问题。在问题描述中我们说过,经过我们的升级预演,我们归纳出了 700+ 类升级导致的不兼容问题。Code Rewrite Flow 就是我们设计出的用于自动处理这些不兼容问题,作出代码改动的组件。
Code Rewrite Flow 是基于 OpenRewrite 的。如图 4 所示,OpenRewrite 是一个用于修改源代码的开源工具,它能够把 Java 代码解析为语法树(Lossless Semantic Trees),并且基于设定的配方(Recipes)去按需地修改该语法树,最终把语法树转译回 Java 代码,从而实现精细修改 Java 代码的能力。

图 4: OpenRewrite 原理
如图 5 所示,在 OpenRewrite 的基础上,我们做了两个重要的优化:
- 结合 eBay 的应用和依赖库的实际情况,针对高频发生的不兼容问题编写了大量自定义的 Recipes。这些 Recipes 和开源社区里的 recipes 一起,组成了我们自动化流程中用于解决 Spring Boot 3 升级中 Simple/Medium 不兼容问题的方案。另外,我们也将其中的一些通用的 Recipes 贡献回到了开源社区。
- 针对我们遇到的 Hard issue(指非常难以修改或是需要大量代码改动的问题,比如需要跨多个文件联动修改,或者需要依赖上下文才能准确修改的情况;例如问题描述中我们提到的:Spring-security 库中自定义 Web Filter 的方式发生了巨大变化),我们创新性地结合了 GenAI 大语言模型来修复这类问题。

图 5: Code Rewrite Flow 架构
自定义 Recipes
通过一个例子来介绍我们编写 Recipe 的效果:对于 servlet 中的 HttpServletRequest 这个类,在从 Spring Boot 2 时代的 javax.servlet.http.HttpServletRequest 升级到 Spring Boot 3 中的 jakarta.servlet.http.HttpServletRequest 时,这个类的组成发生了如下变化:
- 新增了 3 个方法,例如新增了
getProtocolRequestId 方法。
- 删除了 2 个方法,例如删除了
getRealPath 方法。
因此,如果应用和依赖库的代码里继承了这个 HttpServletRequest 类,那么在升级到 Spring Boot 3 时就必须新增或删除对应的方法实现,否则就会导致编译错误。
对于该问题,我们编写了对应的自定义 Recipe,从而能够在我们的自动化流程中自动修复这类问题。该自定义 recipe 的部分代码片段如下图 6 所示,在执行我们的自动化升级流程时,会为要升级的代码执行该 recipe。当要升级的代码包含了 HttpServletRequest 相关的使用时,就会为其自动新增或删除对应的方法实现,从而解决升级带来的编译问题。

图 6: 自定义 Recipe 示例
AI Recipes
AI Recipe 是我们自动化方案里最大的创新点,它结合了 OpenRewrite 和 GenAI 的优点:OpenRewrite 是精确的、快速的,而大语言模型是创造性的、强大的,这两者结合起来就给我们提供了解决复杂不兼容问题的一种思路。

图 7: AI Recipe 原理
图 7 说明了 AI Recipe 的原理:
- 我们首先使用 OpenRewrite 强大的语法解析能力把 Java 源码解析为语法树,并且去定位其中包含哪些可能的问题。基于这些问题,把一个完整的 Java 文件分割为若干代码片段(Code Snippet),每个代码片段最多有一个不兼容的问题。
- 对每一种不兼容的问题,我们都准备了 AI recipe,其主要包含一个 prompt 用于提供给大语言模型。
- 接下来,我们将一个有问题的代码片段及其对应的 AI Recipe 都交给大语言模型,让其帮忙修复这个有问题的代码片段。由于这个代码片段是经过了 OpenRewrite 裁剪过的,因此通常比较短且问题单一,在 AI Recipe 的提示下大语言模型的修复准确率是很高的。此外我们还添加了递归操作,如果第一次大语言模型修复的结果无法通过编译,会回到最开始的阶段重新进行修复。
- 如果存在多个有问题的代码片段,它们会被并行地交给大语言模型进行处理。这样就得到了若干个修复后的代码片段。最后,我们再次使用 OpenRewrite 的能力把这些修复后的代码片段重新组合成一个完整的 Java 源文件,这样就完成了一个完整的 Java 文件的 Spring Boot 3 升级。
图 8 举例说明了 AI Recipe 的组成,该 AI Recipe 用于修复前面我们提到过的 Spring-security 的问题。AI Recipe 中最重要的部分是 prompts,其使用自然语言的形式描述了该类问题该用什么样的方式进行修复。除了 prompts 之外,AI recipe 还包含 conditions 用于帮助定位问题,imports 用于描述修复该问题需要新增/删除哪些 imports,以及 artifacts 用于描述除了 Java 代码之外,修复该问题还需要对其他类型文件(例如 pom)做哪些额外的改动。

图 8: AI Recipe 举例
使用 AI Recipe 带来的好处是显而易见的:一个大的 Java 文件通常存在多处问题,一个复杂的问题也可能需要多处修改才能彻底修复。使用 AI Recipe 的方法可以化繁为简、化整为零:使用 OpenRewrite 把复杂的问题拆解为多个简单的问题,通过大语言模型去创造性地解决这些问题,最终利用 OpenRewrite 的能力去组合回一个 Java 文件。

表格 1: 传统 Recipe 和 AI Recipe 对比
如表格 1 所示,相比于传统的 Recipe,AI Recipe 在编写成本、通用性等方面都要优于传统的 Recipe。事实上,在我们的实践中,我们遇到的绝大部分复杂的问题都是使用 AI Recipe 的方式进行自动化解决的。对于复杂问题来说,编写一个 AI Recipe 的成本也远远低于编写一个普通的 Recipe。而对于不那么复杂的问题,使用传统 Recipe 可以有更快的执行速度,因此我们结合这两种手段,合力组成了 Code Rewrite Flow 的内核。
我们为 eBay 的 Spring Boot 升级编写了超过 600 个自定义 Recipe 和超过 70 个 AI recipe,这些 Recipe 通过 Code Rewrite Flow 的架构高效且自动地实现了对应用和依赖库的代码修改。同时,我们从这些 Recipe 中提取出一些对业界通用的 Recipe,贡献回 OpenRewrite 开源社区。截止目前,我们已经向社区贡献了超过 30 个 Pull Request。此外,我们对于 AI Recipe 的创新也得到了开源社区的认可和支持。如果你想了解更多技术细节和最佳实践,可以访问 云栈社区 上的相关讨论。
Jakarta Converter: Jakarta EE 转换工具
应用依赖于诸多依赖库,而在“痛点与挑战”中我们介绍过依赖库的两个特点:数量众多,以及对于大部分依赖库,其在升级 Spring Boot 3 时需要做的改造仅限于 Java EE -> Jakarta EE 的升级。因此,我们要面对的最大困难是:是否能够快速、自动地为依赖库产生一个 Jakarta EE 的版本,从而不阻塞应用的升级?
答案是肯定的。我们探索出一种完全自动化的方案:由于 Apache 的 Jakarta EE migration tool 可以将一个使用了 Java EE 的依赖库的字节码转换为基于 Jakarta EE 的字节码,因此我们考虑基于该 migration tool 实现一种将依赖库(及其嵌套的依赖)全部自动转化为 Jakarta EE 版本的方案。
图 9 说明了这种方案的原理:对于要转换的依赖库,先计算其不同层级的依赖,然后从最底层的依赖开始,逐级地将所有层级的依赖库全部使用 Apache Jakarta EE migration tool 转化为 Jakarta EE 版本,并且自动上传到 eBay 的 Maven Repository。这样就为一个依赖库产生了 Spring Boot 3 兼容的版本,且不需要依赖库的开发者的修改成本,从而减少了对后续应用升级的阻塞。
在我们的 Spring Boot 3 升级实践中,使用 Jakarta Converter 工具高效、自动地转换了数以万计的依赖库,为整体的 Spring Boot 3 升级大大减少了工作量。

图 9: Jakarta Converter 工作流程
Library Management: 标准化依赖库管理
Jakarta Converter 能够将只有 Java EE 不兼容问题的依赖库进行自动升级,但是仍然有一些复杂的依赖库除了依赖 Java EE 之外,还依赖一些废弃的 Spring Boot 2 接口。这些依赖库就需要和应用一样进行代码层面的升级和修改,也需要开发者遵循标准的修改、测试、发布的流程进行迭代,从而产生新的兼容 Spring Boot 3 的版本供应用使用。
在问题描述中我们说过,应用和依赖库之间的依赖关系是错综复杂的,而依赖库的数目众多、维护成本高等特性也决定了在 Spring Boot 3 升级中依赖库的升级会是一大难点。如何知道哪些依赖库需要升级、在依赖库升级之后如何让相关的应用能够感知到,这些都是我们要解决的问题。

图 10: Library Management 功能
基于这些问题,我们打造了 Library Management Service & UI,致力于把依赖库的版本管理标准化,它主要有以下几个功能:
- 记录依赖库的基本信息、开发者、Spring Boot 3 兼容版本等,提供 UI 交互界面供依赖库的开发者使用。
- 扫描哪些依赖库被应用使用到,为这些依赖库注册基本信息。
- 扫描哪些依赖库存在 Spring Boot 3 兼容性问题,生成兼容性报告,发送邮件给开发者。
- 周期性地扫描依赖库有哪些新版本,并且验证这些新版本的 Spring Boot 3 兼容性情况。
- 提供 API 接口给 Code Rewrite Flow 使用。这样在 Code Rewrite Flow 修改 pom 代码时,可以通过查询该 API 接口来获取一个依赖库对应的 Spring Boot 3 兼容的版本号。
通过 Library Management 组件,我们成功管理了大量的依赖库及其升级之后的版本号,为应用的升级提供了有力的支撑。
Part 4:总结与展望
目前,我们的 Spring Boot 3 自动化无感升级方案已经帮助 eBay 的绝大部分应用成功升级到了基于 Spring Boot 3 的框架。同时,伴随着应用的升级,也有数万个依赖库被升级到了 Spring Boot 3 版本。这些应用和依赖库在提高了安全性的同时,也能够享受更多新技术带来的益处。
基础设施的演进和升级是需要持续维护的。后续,我们的基础设施还面临着更多的升级挑战,如 JDK 升级、Spring Boot 4 升级等等。我们正在积极探索新的解决方案,结合本次 Spring Boot 3 升级的经验,以及正引领潮流的 agentic workflow 等新技术,我们希望打造出一个更强大、更通用、更自动化的代码演化平台。
这个平台的构想是:将任意的代码变动的需求(例如框架升级、无用代码清理等等)描述为输入和输出,并且通过 LLM 分析、脚本扫描等方式预先加载升级前和升级后的期望状态变化。而 agentic workflows 可以将这些信息作为上下文,并且使用不同的 sub agents 自动地进行问题分析、模块整合、代码修改、测试评估、迭代修复等不同的步骤,最终自动地输出符合目标输出的新代码。我们正在积极地探索该方案,期待后续能够和大家分享。
我们相信,通过发挥我们的优势和创新能力,我们可以持续为 eBay 提供高效、可靠的基础设施,为业务的持续创新和发展提供支持。