鹰哥哥 發表於 2025-12-6 21:40:00

从 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生产环境迁移实战

<h1 id="从-spring-boot-2x-到-35x--jdk21一次完整的生产环境迁移实战">从 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生产环境迁移实战</h1>
<p>@</p><div class="toc"><div class="toc-container-header">目录</div><ul><li>从 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生产环境迁移实战</li><li>升级背景</li><li>升级目标与核心变化</li><li>完整升级步骤<ul><li><font style="color: rgba(52, 73, 94, 1)">第一阶段:准备工作(JDK 8 环境)</font><ul><li><font style="color: rgba(85, 85, 85, 1)">代码分支管理</font></li><li><font style="color: rgba(85, 85, 85, 1)">引入 OpenRewrite Maven 插件</font></li><li><font style="color: rgba(85, 85, 85, 1)">执行自动化迁移</font></li><li><font style="color: rgba(85, 85, 85, 1)">OpenRewrite 自动完成的变更</font></li><li><font style="color: rgba(85, 85, 85, 1)">增量合并场景处理</font></li></ul></li><li>第二阶段:环境切换(JDK 21 环境)<ul><li>6. 修改 IDEA 项目配置</li></ul></li></ul></li><li>核心问题与解决方案<ul><li>问题一:Hibernate DDL Auto 的陷阱<ul><li><font style="color: rgba(85, 85, 85, 1)">解决方案</font></li></ul></li><li><strong><font style="color: rgba(52, 73, 94, 1)">问题二:Spring Security 配置迁移</font></strong></li><li><strong><font style="color: rgba(52, 73, 94, 1)">问题三:SpringDoc OpenAPI 配置</font></strong></li><li>问题四:依赖冲突与安全漏洞修复</li><li>问题五:URL 尾斜杠匹配策略变更</li><li><strong><font style="color: rgba(52, 73, 94, 1)">问题六:Apache POI / EasyExcel 升级</font></strong></li><li><font style="color: rgba(52, 73, 94, 1)">问题七:JDK 模块化限制(--add-opens)</font></li><li><font style="color: rgba(51, 51, 51, 1)">问题八:过期配置属性警告</font></li></ul></li><li><strong><font style="color: rgba(52, 73, 94, 1)">完整测试清单</font></strong></li><li>升级感悟</li><li>最后:</li></ul></div><p></p>
<h1 id="升级背景">升级背景</h1>
<p><font style="color: rgba(51, 51, 51, 1)">在私有化部署过程中,客户使用安全扫描工具检测到大量安全漏洞,主要集中在: </font></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">框架版本过低</font></strong><font style="color: rgba(51, 51, 51, 1)">:Spring Boot 2.1.6.RELEASE(发布于 2019 年) </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">JDK 版本过旧</font></strong><font style="color: rgba(51, 51, 51, 1)">:JDK 8(缺乏最新安全补丁) </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">第三方依赖</font></strong><font style="color: rgba(51, 51, 51, 1)">:多个依赖存在已知 CVE 漏洞 </font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)">基于安全合规和长期维护的考虑,决定进行大版本升级。 </font></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">当前版本</font></strong><font style="color: rgba(51, 51, 51, 1)">:Spring Boot 2.1.6.RELEASE + JDK 8 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">目标版本</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font><strong><font style="color: rgba(44, 62, 80, 1)">Spring Boot 3.5.4 + JDK 21 LTS</font></strong></li>
</ul>
<h1 id="升级目标与核心变化">升级目标与核心变化</h1>
<p><strong><font style="color: rgba(52, 73, 94, 1)">主要变化</font></strong></p>
<table>
<thead>
<tr>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">类别</font></strong></th>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">变化内容</font></strong></th>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">迁移方式</font></strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">命名空间</font></strong></td>
<td style="text-align: left">javax.* → jakarta.*</td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">自动化迁移</font></td>
</tr>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">JDK 版本</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">Java 8 → Java 21 LTS</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">自动化迁移 + 手动调整</font></td>
</tr>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">第三方依赖</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">大量依赖需要升级</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">手动处理</font></td>
</tr>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">API 文档</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">Swagger 2.x → SpringDoc OpenAPI 3.x</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">配置调整</font></td>
</tr>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">安全配置</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">WebSecurityConfigurerAdapter 废弃</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">重写配置类</font></td>
</tr>
</tbody>
</table>
<p><strong><font style="color: rgba(52, 73, 94, 1)">为什么选择自动化迁移?</font></strong><br>
<font style="color: rgba(51, 51, 51, 1)">前两项(命名空间和 JDK 版本)涉及的代码改动量极大,手动修改容易出错且效率低下。</font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">OpenRewrite </font></strong><font style="color: rgba(51, 51, 51, 1)">作为业界成熟的自动化重构工具,可以完成大部分繁琐工作。</font></p>
<h1 id="完整升级步骤">完整升级步骤</h1>
<h2 id="第一阶段准备工作jdk-8-环境"><font style="color: rgba(52, 73, 94, 1)">第一阶段:准备工作(JDK 8 环境)</font></h2>
<h3 id="代码分支管理"><font style="color: rgba(85, 85, 85, 1)">代码分支管理</font></h3>
<p><strong><font style="color: rgba(85, 85, 85, 1)"></font></strong></p>
<pre><code class="language-shell"># 确保主分支代码为最新
git checkout dev
git pull origin dev


# 创建升级专用分支
git checkout -b upgrade/springboot3-jdk21
</code></pre>
<h3 id="引入-openrewrite-maven-插件"><font style="color: rgba(85, 85, 85, 1)">引入 OpenRewrite Maven 插件</font></h3>
<p><strong><font style="color: rgba(85, 85, 85, 1)"></font></strong></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">什么是 OpenRewrite? </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">OpenRewrite 是一个自动化代码重构和迁移工具,专为 Java 生态系统设计。 </font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">核心优势</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<ol>
<li><strong><font style="color: rgba(44, 62, 80, 1)">精确安全</font></strong><font style="color: rgba(51, 51, 51, 1)">:在 AST(抽象语法树)层面操作,不会破坏代码结构 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)"> </font><strong><font style="color: rgba(44, 62, 80, 1)">批量处理</font></strong><font style="color: rgba(51, 51, 51, 1)">:一次性处理整个代码库 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">可预览</font></strong><font style="color: rgba(51, 51, 51, 1)">:使用 <code>rewrite:dryRun</code> 查看变更预览 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">可定制</font></strong><font style="color: rgba(51, 51, 51, 1)">:支持声明式(YAML)或编程式自定义规则</font></li>
</ol>
<p><strong><font style="color: rgba(44, 62, 80, 1)">工作原理</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">OpenRewrite 通过解析源代码生成无损语法树(LST),在 AST 层面进行精确转换,完整保留: </font></p>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">原始格式和缩进 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">所有注释 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">代码风格</font></li>
</ul>
<p><strong><font style="color: rgba(44, 62, 80, 1)">配置方式</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<p>在 <code>pom.xml</code> 的 <code>&lt;plugins&gt;</code> 节点下添加:</p>
<pre><code class="language-xml">&lt;plugin&gt;
      &lt;groupId&gt;org.openrewrite.maven&lt;/groupId&gt;
      &lt;artifactId&gt;rewrite-maven-plugin&lt;/artifactId&gt;
      &lt;version&gt;6.15.0&lt;/version&gt;
      &lt;configuration&gt;
            &lt;exportDatatables&gt;true&lt;/exportDatatables&gt;
            &lt;activeRecipes&gt;
                &lt;!-- 升级到 Java 21 --&gt;
                &lt;recipe&gt;org.openrewrite.java.migrate.UpgradeToJava21&lt;/recipe&gt;
                &lt;!-- JUnit 4 to 5 --&gt;
                &lt;recipe&gt;org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration&lt;/recipe&gt;
                &lt;!-- Spring Boot 3.4(插件暂不支持 3.5,升级后手动改) --&gt;
                &lt;recipe&gt;org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_4&lt;/recipe&gt;
            &lt;/activeRecipes&gt;
      &lt;/configuration&gt;
      &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;org.openrewrite.recipe&lt;/groupId&gt;
                &lt;artifactId&gt;rewrite-migrate-java&lt;/artifactId&gt;
                &lt;version&gt;3.14.1&lt;/version&gt;
                &lt;scope&gt;runtime&lt;/scope&gt;
            &lt;/dependency&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;org.openrewrite.recipe&lt;/groupId&gt;
                &lt;artifactId&gt;rewrite-spring&lt;/artifactId&gt;
                &lt;version&gt;6.11.1&lt;/version&gt;
            &lt;/dependency&gt;
      &lt;/dependencies&gt;
    &lt;/plugin&gt;
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">配方(Recipe)说明</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<ul>
<li><code>UpgradeSpringBoot_3_4 :</code>升级至 Spring Boot 3.4.x(插件暂不支持 3.5,升级后手动修改版本号即可) </li>
<li><code>UpgradeToJava21 :</code><font style="color: rgba(51, 51, 51, 1)"> 升级至 JDK 21(Spring Boot 配方仅升级到 JDK 17,需额外添加此配方) </font></li>
<li><code>SpringBoot2JUnit4to5Migration :</code>升级测试框架,避免自动化测试报错</li>
</ul>
<blockquote>
<p><em><strong><font style="color: rgba(44, 62, 80, 1)">提示</font></strong><font style="color: rgba(85, 85, 85, 1)">:你也可以编写自定义配方来处理项目特定的迁移需求。</font></em></p>
</blockquote>
<p><em><font style="color: rgba(85, 85, 85, 1)"></font></em></p>
<h3 id="执行自动化迁移"><font style="color: rgba(85, 85, 85, 1)">执行自动化迁移</font></h3>
<p><strong><font style="color: rgba(85, 85, 85, 1)"></font></strong></p>
<pre><code class="language-shell">mvn rewrite:run
</code></pre>
<p><font style="color: rgba(51, 51, 51, 1)">或者在 IDEA 中通过 Maven 面板执行:</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959429-1550856516.png"></p>
<p><strong>执行时间:</strong> 几分钟到几十分钟不等,取决于项目规模。</p>
<p><strong>可能遇到的问题:</strong></p>
<ol>
<li>如果某些类包含特殊代码导致报错,可以先注释掉,待升级完成后再处理</li>
<li>执行完成后可以删除该插件(也可以保留,以便后续增量升级)</li>
</ol>
<h3 id="openrewrite-自动完成的变更"><font style="color: rgba(85, 85, 85, 1)">OpenRewrite 自动完成的变更</font></h3>
<p><font style="color: rgba(51, 51, 51, 1)">执行完成后,主要变化包括:</font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">依赖升级</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<ul>
<li><code>pom.xml</code>中的依赖版本自动升级 </li>
<li><font style="color: rgba(51, 51, 51, 1)">Spring Boot 版本升级到 3.4.x(手动改为 3.5.4)</font></li>
</ul>
<p><strong><font style="color: rgba(44, 62, 80, 1)">包名变更</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<ul>
<li><code>javax.servlet.* → jakarta.servlet.*</code></li>
<li><code>javax.persistence.* → jakarta.persistence.* </code></li>
<li><code>javax.validation.* → jakarta.validation.*</code></li>
</ul>
<p><strong><font style="color: rgba(44, 62, 80, 1)">API 文档迁移</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">Swagger 2.x → SpringDoc OpenAPI 3.x</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">JDK 新特性应用</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">Text Blocks</font></strong><font style="color: rgba(51, 51, 51, 1)">:多行字符串的优雅处理</font></li>
</ul>
<pre><code class="language-java">// 自动转换为
String json = """
{
"name": "user",
"age": 18
}
""";
</code></pre>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">instanceof 模式匹配</font></strong><font style="color: rgba(51, 51, 51, 1)">:简化类型判断和转换</font></li>
</ul>
<pre><code class="language-java">if (obj instanceof String s) {
       System.out.println(s.toUpperCase());
}
</code></pre>
<ul>
<li><strong>String.formatted():</strong> 替代 <code>String.format()</code></li>
</ul>
<pre><code class="language-java">"Hello, %s!".formatted(name);
</code></pre>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">集合增强</font></strong><font style="color: rgba(51, 51, 51, 1)">: getFirst() 替代 get(0)</font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">@Serial</font></strong><font style="color: rgba(51, 51, 51, 1)"> 注解:标记序列化相关字段</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">第三方库升级</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">Apache HttpClient </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">Apache Commons 系列 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">其他常用工具库</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<h3 id="增量合并场景处理"><font style="color: rgba(85, 85, 85, 1)">增量合并场景处理</font></h3>
<p><strong>场景:</strong> 执行 Rewrite 后,旧分支又有代码提交,合并时出现大量 <code>javax</code> 包名和 Swagger 注解冲突。</p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">解决方案</font></strong><font style="color: rgba(51, 51, 51, 1)">:使用 IntelliJ IDEA 自带的 Refactor 功能(本质也是基于 OpenRewrite)</font></p>
<p><font style="color: rgba(51, 51, 51, 1)">操作步骤:</font></p>
<ol>
<li>打开 IDEA,选择 <code>Refactor → Migrate Packages and Classes </code></li>
<li><font style="color: rgba(51, 51, 51, 1)">选择迁移规则(javax → jakarta) </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">预览变更并执行</font></li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959473-1352013624.png"></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959408-1346918808.png"></p>
<h2 id="第二阶段环境切换jdk-21-环境">第二阶段:环境切换(JDK 21 环境)</h2>
<blockquote>
<p><strong>重要分界线:以下操作需在 JDK 21 环境下进行。</strong></p>
</blockquote>
<hr>
<h3 id="6-修改-idea-项目配置">6. 修改 IDEA 项目配置</h3>
<p><strong>修改 SDK 和 Language Level(快捷键:Ctrl + Alt + Shift + S):</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959474-108710220.png"></p>
<p><strong>修改 Modules 的 Language Level:</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959404-1882460344.png"></p>
<p><strong>修改 Java Compiler(快捷键:Ctrl + Alt + S):</strong></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959431-1051574455.png"></p>
<hr>
<hr>
<hr>
<h1 id="核心问题与解决方案">核心问题与解决方案</h1>
<h2 id="问题一hibernate-ddl-auto-的陷阱">问题一:Hibernate DDL Auto 的陷阱</h2>
<blockquote>
<p><strong>严重警告:在完成以下配置前,切勿启动项目!否则可能导致数据库结构被错误修改。</strong></p>
</blockquote>
<hr>
<p><strong>问题背景</strong></p>
<p>新旧版本 Hibernate 的行为差异:</p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959473-1968463885.png"></p>
<p><strong><font style="color: rgba(85, 85, 85, 1)">为什么要禁用?</font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">在生产环境中使用</font> <code>spring.jpa.hibernate.ddl-auto=update</code> 存在严重风险:</p>
<ol>
<li><strong><font style="color: rgba(44, 62, 80, 1)">数据安全风险</font></strong><font style="color: rgba(51, 51, 51, 1)">:自动更新可能导致意外的数据丢失或结构变更 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">性能问题</font></strong><font style="color: rgba(51, 51, 51, 1)">:启动时全表检查会显著增加应用启动时间 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">版本控制缺失</font></strong><font style="color: rgba(51, 51, 51, 1)">:无法追踪数据库变更历史,不利于团队协作和回滚 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">升级后风险更高</font></strong><font style="color: rgba(51, 51, 51, 1)">:Hibernate 6.x 的校验更严格,误操作概率增加</font></li>
</ol>
<p><strong><font style="color: rgba(85, 85, 85, 1)"></font></strong></p>
<h3 id="解决方案"><font style="color: rgba(85, 85, 85, 1)">解决方案</font></h3>
<p><strong><font style="color: rgba(44, 62, 80, 1)">方案一:配置优先级控制(推荐)</font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">在 CI/CD 启动脚本中设置 VM 参数:</font></p>
<pre><code class="language-shell">java -jar app.jar -Dspring.jpa.hibernate.ddl-auto=none
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">优先级</font></strong><font style="color: rgba(51, 51, 51, 1)">:VM 参数 &gt; 配置中心(Apollo/Nacos) &gt; application.properties</font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">方案二:使用专业的数据库版本管理工具</font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">推荐使用 </font><strong><font style="color: rgba(44, 62, 80, 1)">Flyway</font></strong><font style="color: rgba(51, 51, 51, 1)"> 或 </font><strong><font style="color: rgba(44, 62, 80, 1)">Liquibase</font></strong><font style="color: rgba(51, 51, 51, 1)"> 管理数据库脚本:</font></p>
<pre><code class="language-xml">&lt;dependency&gt;
   &lt;groupId&gt;org.flywaydb&lt;/groupId&gt;
   &lt;artifactId&gt;flyway-core&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">方案三:结构对比工具</font></strong></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">Navicat</font></strong><font style="color: rgba(51, 51, 51, 1)">:提供结构同步功能 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">DataGrip</font></strong><font style="color: rgba(51, 51, 51, 1)">:IntelliJ 系产品,支持数据库结构对比</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<h2 id="问题二spring-security-配置迁移"><strong><font style="color: rgba(52, 73, 94, 1)">问题二:Spring Security 配置迁移</font></strong></h2>
<p><strong><font style="color: rgba(85, 85, 85, 1)">核心变化</font></strong></p>
<ul>
<li><code>WebSecurityConfigurerAdapter</code> 已废弃</li>
<li><font style="color: rgba(51, 51, 51, 1)">推荐使用 </font><strong><font style="color: rgba(51, 51, 51, 1)">Lambda DSL</font></strong><font style="color: rgba(51, 51, 51, 1)"> 配置方式 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">配置方式从继承改为 Bean 注册</font></li>
</ul>
<p><strong><font style="color: rgba(85, 85, 85, 1)">迁移示例</font></strong></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">旧版配置</font></strong><font style="color: rgba(51, 51, 51, 1)">(Spring Security 5.x):</font></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated();
    }
}
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">新版配置</font></strong><font style="color: rgba(51, 51, 51, 1)">(Spring Security 6.x):</font></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final TokenProvider tokenProvider;
    public SecurityConfig(TokenProvider tokenProvider) {
      this.tokenProvider = tokenProvider;
    }
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
      http
                .csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(sessionManagement -&gt; sessionManagement
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(authorizeRequests -&gt; authorizeRequests
                        // 允许所有 OPTIONS 请求
                        .requestMatchers(OPTIONS, "**").permitAll()
                        .requestMatchers(
                              "/swagger-ui/**",
                              "/v3/api-docs/**",
                              "/swagger-resources/**",
                              "/images/**",
                              "/webjars/**").permitAll()
                        .anyRequest().authenticated())
                .addFilterBefore(new JWTFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class);
      return http.build();
    }
}
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)">RequestMatcher 调整注意事项</font></strong></p>
<ol>
<li><strong><font style="color: rgba(44, 62, 80, 1)">新增 SpringDoc 路径</font></strong><font style="color: rgba(51, 51, 51, 1)">(必须)</font></li>
</ol>
<pre><code class="language-java">/swagger-ui/**

/v3/api-docs/**
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">修正通配符写法</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<pre><code class="language-java">❌ 错误: //**/*.js
✅ 正确: /**/*.js

否则会抛出 PatternParseException

</code></pre>
<h2 id="问题三springdoc-openapi-配置"><strong><font style="color: rgba(52, 73, 94, 1)">问题三:SpringDoc OpenAPI 配置</font></strong></h2>
<p><strong><font style="color: rgba(85, 85, 85, 1)">Swagger → SpringDoc 迁移</font></strong></p>
<pre><code class="language-java">        &lt;!-- 移除旧的 Swagger 依赖 --&gt;
    &lt;!--
    &lt;dependency&gt;
   &lt;groupId&gt;io.springfox&lt;/groupId&gt;
   &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
    &lt;/dependency&gt;
    --&gt;
    &lt;!-- 添加新的 SpringDoc 依赖 --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.springdoc&lt;/groupId&gt;
      &lt;artifactId&gt;springdoc-openapi-starter-webmvc-ui&lt;/artifactId&gt;
      &lt;version&gt;2.3.0&lt;/version&gt;
    &lt;/dependency&gt;
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)">配置示例</font></strong></p>
<pre><code class="language-java">@Configuration
@OpenAPIDefinition
public class SwaggerConfig {
    @Bean
    public OpenAPI openAPI() {
      OpenAPI openAPI = new OpenAPI();
      openAPI.info(new Info().title("API 文档").version("1.0"));
      // 配置 Authorization 登录鉴权
      Map&lt;String, SecurityScheme&gt; map = Map.of("Authorization",
                new SecurityScheme()
                        .type(SecurityScheme.Type.APIKEY)
                        .in(SecurityScheme.In.HEADER)
                        .name("Authorization"));
      openAPI.components(new Components().securitySchemes(map));
      map.keySet().forEach(key -&gt; openAPI.addSecurityItem(new SecurityRequirement().addList(key)));
      return openAPI;
    }
}
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)">注解对应关系</font></strong></p>
<table>
<thead>
<tr>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">Swagger 2.x</font></strong></th>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">SpringDoc OpenAPI 3.x</font></strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Api</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Tag</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@ApiOperation</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Operation</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@ApiParam</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Parameter</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@ApiModel</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Schema</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@ApiModelProperty</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">@Schema</font></td>
</tr>
</tbody>
</table>
<p><strong><font style="color: rgba(85, 85, 85, 1)">访问地址变更</font></strong></p>
<p>原 Swagger UI 地址: <code>http://localhost:8080/swagger-ui.html </code><br>
新 SpringDoc 地址: <code>http://localhost:8080/swagger-ui/index.html</code><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959428-1456900589.png"></p>
<h2 id="问题四依赖冲突与安全漏洞修复">问题四:依赖冲突与安全漏洞修复</h2>
<p><strong>检测工具</strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">使用 IDEA 自带的依赖分析工具:</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959418-636260244.png"></p>
<p><strong><font style="color: rgba(85, 85, 85, 1)">必须升级的依赖(存在高危漏洞) </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">推荐使用 </font><strong><font style="color: rgba(44, 62, 80, 1)">OWASP Dependency-Check</font></strong><font style="color: rgba(51, 51, 51, 1)"> 或 </font><strong><font style="color: rgba(44, 62, 80, 1)">Snyk</font></strong><font style="color: rgba(51, 51, 51, 1)"> 扫描:</font></p>
<pre><code class="language-shell">mvn dependency-check:check
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)">解决依赖冲突的技巧</font></strong></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">问题</font></strong><font style="color: rgba(51, 51, 51, 1)">:Maven 依赖解析采用"最短路径优先"和"第一声明优先"原则,可能导致旧版本覆盖新版本。</font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">解决方案</font></strong><font style="color: rgba(51, 51, 51, 1)">:显式声明期望的版本</font></p>
<pre><code class="language-java">&lt;dependencies&gt;
    &lt;!-- 显式声明 Spring Framework 版本,避免被传递依赖覆盖 --&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-core&lt;/artifactId&gt;
      &lt;version&gt;6.1.3&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">快速检测技巧</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<p>在 IDEA 的 Maven 依赖树中搜索 <code>RELEASE</code> ,Spring 新版本已不使用 <code>RELEASE</code> 后缀,搜索到的基本都是旧版本。</p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959450-1123133955.png"></p>
<h2 id="问题五url-尾斜杠匹配策略变更">问题五:URL 尾斜杠匹配策略变更</h2>
<p><strong><font style="color: rgba(85, 85, 85, 1)">行为变化</font></strong></p>
<table>
<thead>
<tr>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">版本</font></strong></th>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">行为</font></strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">Spring Boot 2.x</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">/api/user/get 和 /api/user/get/ 视为同一接口</font></td>
</tr>
<tr>
<td style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">Spring Boot 3.x</font></strong></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">/api/user/get 和 /api/user/get/ 视为</font><strong><font style="color: rgba(52, 73, 94, 1)">不同接口</font></strong></td>
</tr>
</tbody>
</table>
<p><strong><font style="color: rgba(85, 85, 85, 1)">常见导致尾斜杠的情况</font></strong></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">Case 1:类注解带尾斜杠</font></strong></li>
</ul>
<pre><code class="language-java">@RequestMapping("/api/user/")
public class UserController {
@PostMapping("login") // 实际路径:/api/user/login
}
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">Case 2:空字符串映射</font></strong></p>
<pre><code class="language-java">@RequestMapping("/api/user")
public class UserController {
@PostMapping("") // 实际路径:/api/user/(带尾斜杠!)
}
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">Case 3:根路径映射</font></strong></p>
<pre><code class="language-java">@PostMapping("/") // 实际路径:/(带尾斜杠)
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)"><br>
</font>****<font style="color: rgba(85, 85, 85, 1)"> 检查方式 </font></strong></p>
<ol>
<li><strong><font style="color: rgba(44, 62, 80, 1)">IDEA Endpoints 工具窗口</font></strong><font style="color: rgba(51, 51, 51, 1)">:查看所有端点 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">SpringDoc UI</font></strong><font style="color: rgba(51, 51, 51, 1)">:访问 Swagger 页面检查</font></li>
</ol>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959437-479232744.png"></p>
<p>临时解决方案(不推荐长期使用)</p>
<pre><code class="language-java">import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {      
      // 设 置 为true 以 忽 略 尾 斜 杠 , 恢 复 旧 版 本 行 为
      configurer.setUseTrailingSlashMatch(true);
    }
}
</code></pre>
<blockquote>
<p><em><strong><font style="color: rgba(44, 62, 80, 1)">注意</font></strong>__<font style="color: rgba(85, 85, 85, 1)">: ·</font></em><code>setUseTrailingSlashMatch</code> 在 Spring 6.x 后已标记为废弃,后续版本将删除。建议逐步修正所有端点,去除尾斜杠。</p>
</blockquote>
<p><strong><font style="color: rgba(85, 85, 85, 1)">根本解决方案</font></strong></p>
<ol>
<li><font style="color: rgba(51, 51, 51, 1)">修正所有 Controller 的路径映射 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">通知前端团队同步修改调用路径 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">如果有硬编码的 URL,全局搜索并修正 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">使用测试确保前后端调用正常</font></li>
</ol>
<p><strong><font style="color: rgba(85, 85, 85, 1)"></font></strong></p>
<h2 id="问题六apache-poi--easyexcel-升级"><strong><font style="color: rgba(52, 73, 94, 1)">问题六:Apache POI / EasyExcel 升级</font></strong></h2>
<p><strong><font style="color: rgba(85, 85, 85, 1)">背景 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">Apache POI 旧版本(&lt; 5.0)存在多个 CVE 安全漏洞,必须升级。</font></p>
<p><strong><font style="color: rgba(85, 85, 85, 1)">推荐方案</font></strong></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">对于新项目</font></strong><font style="color: rgba(51, 51, 51, 1)">:直接使用 </font><strong><font style="color: rgba(44, 62, 80, 1)">FastExcel</font></strong></p>
<pre><code class="language-java">&lt;dependency&gt;
   &lt;groupId&gt;cn.idev.excel&lt;/groupId&gt;
   &lt;artifactId&gt;fastexcel&lt;/artifactId&gt;
   &lt;version&gt;1.0.0&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p><strong><font style="color: rgba(44, 62, 80, 1)">对于使用 EasyExcel 的旧项目</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></p>
<pre><code class="language-java">&lt;dependency&gt;
   &lt;groupId&gt;com.alibaba&lt;/groupId&gt;
   &lt;artifactId&gt;easyexcel&lt;/artifactId&gt;
   &lt;version&gt;4.0.3&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<blockquote>
<p><em><font style="color: rgba(85, 85, 85, 1)"></font><strong><strong><font style="color: rgba(44, 62, 80, 1)">说明</font></strong></strong><font style="color: rgba(85, 85, 85, 1)">:EasyExcel 已不再维护,FastExcel 是社区维护的替代方案,API 基本兼容。</font></em></p>
</blockquote>
<p><strong><font style="color: rgba(85, 85, 85, 1)">迁移注意事项</font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">EasyExcel 跨大版本升级(2.x → 4.x)API 变化较大,主要改动: </font></p>
<p><strong><font style="color: rgba(52, 152, 219, 1)">1. </font></strong><font style="color: rgba(51, 51, 51, 1)">监听器接口方法签名调整 </font></p>
<p><strong><font style="color: rgba(52, 152, 219, 1)">2. </font></strong><font style="color: rgba(51, 51, 51, 1)">部分工具类包路径变更 </font></p>
<p><strong><font style="color: rgba(52, 152, 219, 1)">3. </font></strong><font style="color: rgba(51, 51, 51, 1)">自定义转换器需要适配新接口 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">建议参考官方迁移文档:</font><font style="color: rgba(66, 185, 131, 1)">EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网</font></p>
<p><font style="color: rgba(52, 152, 219, 1)"></font></p>
<h2 id="问题七jdk-模块化限制--add-opens"><font style="color: rgba(52, 73, 94, 1)">问题七:JDK 模块化限制(--add-opens)</font></h2>
<p><strong><font style="color: rgba(85, 85, 85, 1)">问题现象 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">某些依赖库使用反射访问 JDK 内部 API,在 JDK 9+ 模块化系统下会报错:</font></p>
<pre><code class="language-java">InaccessibleObjectException: Unable to make field accessible:
module java.base does not "opens java.net" to unnamed module
</code></pre>
<p><strong><font style="color: rgba(85, 85, 85, 1)">解决方案 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">在 IDEA 运行配置中添加 VM 参数: </font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">开启 VM 参数配置</font></strong><font style="color: rgba(51, 51, 51, 1)">(默认隐藏):</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959454-2062859529.png"></p>
<p><strong><font style="color: rgba(85, 85, 85, 1)">解决方案 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">在 IDEA 运行配置中添加 VM 参数: </font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">开启 VM 参数配置</font></strong><font style="color: rgba(51, 51, 51, 1)">(默认隐藏):</font></p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959969-765078803.png"></p>
<p><strong>常见需要开放的模块</strong></p>
<pre><code class="language-java">--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
</code></pre>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<h2 id="问题八过期配置属性警告"><font style="color: rgba(51, 51, 51, 1)">问题八:过期配置属性警告</font></h2>
<p>问题现象</p>
<p>启动时出现警告:</p>
<pre><code class="language-java">Property 'spring.xxx.yyy' is deprecated
</code></pre>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959419-1910101826.png"></p>
<p><strong>解决方案:</strong></p>
<ol>
<li>
<p>查看 Spring Boot 官方迁移文档</p>
</li>
<li>
<p>使用 IDEA 的智能提示查看替代属性</p>
</li>
<li>
<p>修改配置文件( 常见过期属性:<code>application.yml</code> 或配置中心)</p>
</li>
</ol>
<p><font style="color: rgba(51, 51, 51, 1)">常见过期属性:</font></p>
<table>
<thead>
<tr>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">过期属性</font></strong></th>
<th style="text-align: left"><strong><font style="color: rgba(52, 73, 94, 1)">替代属性</font></strong></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">spring.datasource.type</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">自动推断,无需配置</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">已移除</font></td>
</tr>
<tr>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">management.metrics.export.prometheus.enabled</font></td>
<td style="text-align: left"><font style="color: rgba(52, 73, 94, 1)">management.prometheus.metrics.export.enabled</font></td>
</tr>
</tbody>
</table>
<font style="color: rgba(51, 51, 51, 1)">
</font><font style="color: rgba(51, 51, 51, 1)"> </font>
<h1 id="完整测试清单"><strong><font style="color: rgba(52, 73, 94, 1)">完整测试清单</font></strong></h1>
<p><font style="color: rgba(51, 51, 51, 1)">升级完成后,务必进行全面的回归测试: </font></p>
<ul>
<li><strong><font style="color: rgba(44, 62, 80, 1)">Spring Security</font></strong><font style="color: rgba(51, 51, 51, 1)">:认证、授权是否正常 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">SpringDoc</font></strong><font style="color: rgba(51, 51, 51, 1)">:API 文档是否可访问( </font><font style="color: rgba(199, 37, 78, 1)">/swagger-ui/index.html </font><font style="color: rgba(51, 51, 51, 1)">) </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">数据库操作</font></strong><font style="color: rgba(51, 51, 51, 1)">:JPA/MyBatis 是否正常工作 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">缓存</font></strong><font style="color: rgba(51, 51, 51, 1)">:Redis/Caffeine 等缓存是否生效 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">消息队列</font></strong><font style="color: rgba(51, 51, 51, 1)">:RabbitMQ/Kafka 等是否正常 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">定时任务</font></strong><font style="color: rgba(51, 51, 51, 1)">:Scheduled/Quartz 是否按预期执行 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">文件上传/下载</font></strong><font style="color: rgba(51, 51, 51, 1)">:文件 IO 操作是否正常 </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">业务功能</font></strong><font style="color: rgba(51, 51, 51, 1)">:核心业务流程是否正常(重点关注有代码改动的地方) </font></li>
<li><strong><font style="color: rgba(44, 62, 80, 1)">性能测试</font></strong><font style="color: rgba(51, 51, 51, 1)">:对比升级前后的性能指标</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<h1 id="升级感悟">升级感悟</h1>
<p><strong><font style="color: rgba(52, 73, 94, 1)">框架层面的变化趋势 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">通过这次升级,我观察到现代框架的一些发展趋势: </font></p>
<ol>
<li><strong><font style="color: rgba(44, 62, 80, 1)">校验更严格</font></strong><font style="color: rgba(51, 51, 51, 1)">:</font></li>
</ol>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">Spring 不再容忍 URL 尾斜杠的模糊匹配 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">循环依赖检测更严格(默认禁止) </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">Hibernate 对实体状态的校验更精确</font></li>
</ul>
<ol start="2">
<li><strong><font style="color: rgba(44, 62, 80, 1)">安全性优先</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></li>
</ol>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">默认配置更保守 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">废弃不安全的 API </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">强制升级修复已知漏洞</font></li>
</ul>
<ol start="3">
<li><strong><font style="color: rgba(44, 62, 80, 1)">现代化 API</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></li>
</ol>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">Lambda DSL 配置风格 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">函数式编程支持 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">更简洁的 API 设计</font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><strong><font style="color: rgba(52, 73, 94, 1)">依赖选择建议 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">基于这次升级经验,对于第三方库的选择建议: </font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">优先选择</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">✅</font><font style="color: rgba(51, 51, 51, 1)"> 国际主流项目(Apache、Spring 生态等) </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">✅</font><font style="color: rgba(51, 51, 51, 1)"> 有完善文档和测试的项目 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">✅</font><font style="color: rgba(51, 51, 51, 1)"> 活跃维护且社区规模大的项目 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">✅</font><font style="color: rgba(51, 51, 51, 1)"> 语义化版本管理清晰的项目 </font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">谨慎选择</font></strong><font style="color: rgba(51, 51, 51, 1)">: </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">⚠️</font><font style="color: rgba(51, 51, 51, 1)"> 缺乏自动化测试的项目 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">⚠️</font><font style="color: rgba(51, 51, 51, 1)"> 长期未更新的项目 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">⚠️</font><font style="color: rgba(51, 51, 51, 1)"> API 设计不稳定、频繁 Breaking Change 的项目 </font></p>
<p><font style="color: rgba(51, 51, 51, 1)">⚠️</font><font style="color: rgba(51, 51, 51, 1)"> 文档不全、维护团队不稳定的项目</font></p>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><strong><font style="color: rgba(52, 73, 94, 1)">自动化迁移的价值 </font></strong></p>
<p><font style="color: rgba(51, 51, 51, 1)">OpenRewrite 等自动化工具在大版本升级中的价值无可替代: </font></p>
<ul>
<li><font style="color: rgba(51, 51, 51, 1)">减少 90% 以上的机械性改动 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">避免手工替换导致的遗漏 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">保持代码风格和注释 </font></li>
<li><font style="color: rgba(51, 51, 51, 1)">降低升级风险 </font></li>
</ul>
<p><font style="color: rgba(51, 51, 51, 1)">建议在日常开发中也关注此类工具,提升团队整体效率。</font></p>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><font style="color: rgba(51, 51, 51, 1)"></font></p>
<p><strong><font style="color: rgba(44, 62, 80, 1)">本文首发于 [尚硅谷编程联盟],转载请注明出处。</font></strong></p>
<h1 id="最后">最后:</h1>
<blockquote>
<p>“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”</p>
<p><img src="https://img2024.cnblogs.com/blog/3084824/202512/3084824-20251206213959471-1115105071.gif"></p>
</blockquote><br><br>
来源:https://www.cnblogs.com/TheMagicalRainbowSea/p/19316680
頁: [1]
查看完整版本: 从 Spring Boot 2.x 到 3.5.x + JDK21:一次完整的生产环境迁移实战