踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

最近正在给自己的开源项目校园博客升级到 JDK17 以及 SpringBoot3,正好记录下升级和踩坑的过程,给大家提供一些解决方案的参考。

先说结论:非常推荐升级JDK17,成本低收益高。至于SpringBoot3.0,迁移成本比较高,坑也会比较多,但如果是新项目的话,还是可以试试的。

PS:项目原来的版本是 JDK8 + SpringBoot2.6。

为什么要升级?

  • JDK17和SpringBoot3也发布了一段时间了,自己对一些新特性也比较感兴趣,尤其是 Native Image 这个玩意。
  • 自己手上刚好有校园博客这个项目,可以用来给进行升级,项目不复杂,但也算五脏俱全,全量升级既可以感受一下变化,也不会太费事。
  • JDK17 是一个长期支持的版本(LTS),现在很多开源应用或者一些组件都在往这上面靠,并且大有一种最低支持 JDK17 的趋势。
  • 自己在公司所接触到的项目也有一部分是使用的JDK17,并且整体有往这方面靠的趋势,新项目都会直接用JDK17。

总的来说就是 兴趣 + 资源 + 趋势。

升级有什么好处?

先来看看 JDK8 -> JDK17 的好处。

  • ZGC垃圾回收器,性能提升
  • 可以使用 var 作为局部变量类型推断标识符
  • 一个文件中可以包含多个public类
  • switch 使用起来更加简洁,可以不用再break了。
  • instanceof 增强
  • 增加不可修改的数据类 record(感觉还是 kotlin 的 data class 好用)
  • Text Blocks文本块

实用性很强,非常舒服。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

再看看 SpringBoot3.0 的一些新特性。

  • 更好的支持 Native Image,使用 GraalVM 构建原生镜像,可以提供显著的内存和启动性能改进
  • 升级到 Spring6.0
  • 升级到 Spring Security 6.0
  • ……

好吧,感觉上是不如 JDK17 要更有性价比,如果对 Native Image 兴趣不大的话,建议不要升级SpringBoot3.x,因为升级SpringBoot的成本可要比升级JDK高多了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

升级过程分享

以下的一切内容均基于我已有的项目【校园博客】进行升级和讲解,源码地址:https://github.com/stick-i/scblogs

既然一切都是基于JDK17的,那我们就先升级JDK吧!

升级JDK17

下载安装

安装JDK17,这里我直接在IDEA里面下载安装了,很方便:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

为了便于自己以后使用 Native Image,这里我直接下载了 GraalVM。

在IDEA中更新项目SDK和模块SDK:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

Maven构建

更新Maven编译配置:

<properties>
  <java.version>17</java.version>
  <maven.compiler.source>${java.version}</maven.compiler.source>
  <maven.compiler.target>${java.version}</maven.compiler.target>
</properties>

Maven重新打包下:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

这一步主要是为了更新下内部组件的 JDK 版本。

启动服务

测试下有没有其他问题,启动所有微服务项目:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

竟然一切正常,也可能与我的项目比较简单有一定的关系,所有服务都成功跑起来了。

更新Dockerfile

原来使用的基础镜像是 java:8-alpine,更新到了亚马逊的openjdk17版本amazoncorretto:17-alpine

# 设置JAVA版本
FROM amazoncorretto:17-alpine
# 指定存储卷, 任何向/tmp写入的信息都不会记录到容器存储层
VOLUME /tmp
# 拷贝运行JAR包
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
# 设置JVM运行参数,限定内存大小,并设置时区为东八区
ENV JAVA_OPTS="
-server 
-Xms256m 
-Xmx512m 
-XX:MetaspaceSize=256m 
-XX:MaxMetaspaceSize=512m 
-Duser.timezone=GMT+08 "

#空参数,方便创建容器时传参
ENV PARAMS=""
# 入口点, 执行JAVA运行命令
ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /app.jar $PARAMS"]

源代码

看起来没什么问题了,先提交上JDK升级的代码,有需要的同学可以查看提交记录:https://github.com/stick-i/scblogs/pull/198/commits

升级SpringBoot3.2

为什么选择直接升级到 SpringBoot3.2 而不是 3.0呢?

主要是我开始升级的时候,SpringBoot已经更新到3.2了,而此时的3.0的生命周期已经过半了,目前也还没有推出3.0以上的LTS版本,这么看来我以后总还是要升级的,倒不如现在一起弄了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

升级pom依赖

跟SpringBoot相关的依赖还是比较多的,尤其是依赖了SpringCore的三方依赖,肯定也是要统一进行升级的。

截至到我写这篇文章的时间,SpringBoot的最新GA版本为 3.2.1:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

我选择相信Spring,直接升级最新版!

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

对应的SpringCloud版本为2023.0.0

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

其他主要依赖对应升级的情况:

依赖项 升级前版本 升级后版本 备注
SpringBoot 2.6.11 3.2.1 目前的最新版,要踩坑就踩最新的坑🤡
SpringCloud 2021.0.4 2023.0.0 对应SpringBoot3.2.x
SpringCloudAlibaba 2021.0.4.0 2022.0.0 这个库还没出2023的版本,但是2022版也是基于SpringBoot3.0的,应该不会差太多
Mybatis-Plus 3.5.3.1 3.5.5 注意:artifactId 从mybatis-plus-boot-starter改成了mybatis-plus-spring-boot3-starter
druid 1.2.11 1.2.20 注意:artifactId 从druid-spring-boot-starter改成了druid-spring-boot-3-starter

对了,建议顺便升级下Maven。

解决依赖异常

修改完pom文件之后刷新一下本地依赖包,噢呦,一堆报错:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

我看了一下,就两个问题,分别是 mysql-connector-java 和 javax.servlet-api 这两个包的版本没有被指定,所以Maven找不到对应的包。

为什么没有指定呢?之前也没有指定,但是之前没有报错,说明这两个包之前是有被 spring-boot-starter-parent 所管理的,但是现在它不管了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

这得去看看SpringBoot3.0的迁移文档:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide

MySQL

在网页里搜索关键字 MySQL,这不就来了:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

就是说 mysql:mysql-connector-java 这个包的坐标改成了 com.mysql:mysql-connector-j,让我们更新的时候也顺带改一下。这个简单,全局搜索然后改一下就好了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

一改完,版本继承的小图标就出来了,不错不错。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

javax -> jakarta

然后再搜一下关键字 javax,这不就又来了:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

这个就稍微麻烦一点了,不仅Maven坐标从 jakarta.servlet:jakarta.servlet-api 改成了 javax.servlet:javax.servlet-api,而且包名也从 javax.xxx.xxx 改成了 jakarta.xxx.xxx,所有导入了 javax 的包都得改。

先更新下pom文件:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

然后再全局搜索 javax 替换下:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

我试过了,升级完后唯一出现问题的地方就只有一处,但也很容易修改:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

ResponseStatusException 中没有 getStatus() 这个方法了,我使用HttpStatus.valueOf(statusException.getStatusCode().value()) 代替了原来的方法。

做完上面这些后,我的项目已经可以成功编译了,但还不能正常的跑起来。

配置文件属性迁移

SpringBoot3 更改了一些配置属性,例如:spring.redis.host改为了spring.data.redis.host

这一变更几乎对所有项目都会有影响,要查看所有的变更项,可以在官方文档中进行查找:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Configuration-Changelog

但这太silly了,很显然官方也这么认为,所以给开发者提供了一个简单的迁移方法,**引入 **spring-boot-properties-migrator** **,它会帮你自动检测配置文件中需要修改的地方:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
  <scope>runtime</scope>
</dependency>

配置文件属性迁移完毕后,记得删除这里添加的 spring-boot-properties-migrator 依赖。

然后运行项目,当然你的项目大概率是运行不起来的,但别着急,看看你的控制台输出,有没有像我这样的输出内容:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

上面的异常信息其实分为了两个部分,前面部分是需要进行修改的配置:

The use of configuration keys that have been renamed was found in the environment:

Property source ‘bootstrapProperties-default-redis.yaml,DEFAULT_GROUP’:

** Key: spring.redis.host**

** Replacement: spring.data.redis.host**

** Key: spring.redis.password**

** Replacement: spring.data.redis.password**

** Key: spring.redis.port**

** Replacement: spring.data.redis.port**

Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.

它也给出了重命名之后的key,这里直接对着描述把自己的配置文件改改就好了,比较简单。


后面部分是说有一些配置已经被弃用了,但是它也给出了弃用的原因:

The use of configuration keys that are no longer supported was found in the environment:

Property source ‘bootstrapProperties-default-springmvc.yaml,DEFAULT_GROUP’:

Key: spring.mvc.throw-exception-if-no-handler-found

** Reason: DispatcherServlet property is deprecated for removal and should no longer need to be configured**

Property source ‘bootstrapProperties-default-redis.yaml,DEFAULT_GROUP’:

** Key: spring.redis.lettuce.pool.max-active**

** Reason: none**

** Key: spring.redis.lettuce.pool.max-idle**

** Reason: none**

** Key: spring.redis.lettuce.pool.max-wait**

** Reason: none**

** Key: spring.redis.lettuce.pool.min-idle**

** Reason: none**

Please refer to the release notes or reference guide for potential alternatives.

好吧,这里其实有点小坑,只有上面第一个Key给了弃用原因,说是DispatcherServlet属性已经被移除了。但是后面几个redis相关的Key都是没有给弃用原因的。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

既然这样,那我只能自己去官方文档里找了:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Configuration-Changelog

全局搜索下 lettuce.pool,你别说,还真让我找到了:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

明明也没有弃用,就是把redis前面加个data罢了,看来 spring-boot-properties-migrator也偶有瞎说的情况啊。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

再次提醒:配置文件属性迁移完毕后,记得删除之前添加的 spring-boot-properties-migrator 依赖。

ES版本兼容

如果你的es客户端版本和es服务端版本一致(均为8.x),可以直接跳过这部分内容。

项目里使用了 spring-boot-starter-data-elasticsearch,升级SpringBoot3.x 之后,这个依赖的版本也·提高了,对应ES的版本是8.x,而我服务器使用的ES版本是7.x,所以有一些不兼容的问题,启动时出现异常:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

Caused by: java.lang.RuntimeException: node: http://xxxxxx, status: 200, [es/indices.exists] Missing [X-Elastic-Product] header. Please check that you are connecting to an Elasticsearch instance, and that any networking filters are preserving that header.

本来想通过降低 elasticsearch-rest-client 的版本来解决这个问题,但是降低之后又不能兼容 SpringBoot3 了,于是只能另辟蹊径了。

这个说起来比较麻烦,我在 stackoverflow 上找到一篇帖子,里面有对这个问题的描述,可以参考下:https://stackoverflow.com/questions/71142680/co-elastic-clients-transport-transportexception-es-search-missing-x-elastic

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

它讲到了两个问题:

  1. 客户端向服务端发送了未知的 Content-Type ,因此其请求被拒绝并返回 406(其实是请求头 compatible-with 不受支持)
  2. 客户端需要验证 response 中是否具有 X-Elastic-Product=Elasticsearch 标头,但服务端并没有返回这个。

问题其实蛮清晰的,但是给出的解决方案让我不太满意,还需要自己重新去构建一个 RestClient,自己读取配置文件然后set进去,又得设置账号密码、又得解析Host的,这我可受不了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

于是经过我的一顿研究之后,我发现了 RestClientBuilderCustomizer 这个类:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

翻译:回调接口,可以由希望通过RestClientBuilder进一步定制RestClient的bean实现,同时保留默认的自动配置。

只要用这个玩意,就可以在原有的 RestClient 基础上,进行一些定制化的操作,比如说解决上面那两个问题。于是乎,我就写了下面这一段代码:

/**
 * Es 兼容性配置,添加响应头,兼容服务端版本
 * <p>
 * 如果客户端与服务端版本一致,可移除此配置。
 *
 * @author 阿杆
 * @version 1.0
 * @date 2024/1/25 22:29
 */

@Component
public class EsCompatibilityConfig implements RestClientBuilderCustomizer {

 @Override
 public void customize(RestClientBuilder builder) {
 }

 @Override
 public void customize(HttpAsyncClientBuilder builder) {
  // 添加响应头,兼容X-Elastic-Product
  HttpResponseInterceptor httpResponseInterceptor =
    (response, context) -> response.addHeader("X-Elastic-Product""Elasticsearch");
  builder.addInterceptorLast(httpResponseInterceptor);
  // 自定义默认请求头,目的是禁用兼容性请求头 compatible-with
  builder.setDefaultHeaders(List.of(new BasicHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())));
 }

}

这段代码很简单,在构建 RestClient 的过程中插入了一段代码,修改了请求头和响应头,用来兼容ES版本。只需要把这个类注入到Spring Bean中,就可以被 ElasticsearchRestClientConfigurations 自动加载。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

WARN trationDelegate$BeanPostProcessorChecker: is not eligible for getting processed….

如图所示,我的项目在升级到 SpringBoot3.x 后出现了大量的 WARN:

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

虽然不影响项目运行,但是看得我很不爽,那只能想想办法看怎么解决掉这个warn了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

随机截取的一段异常信息,其他的也都差不多:2024-01-28T11:58:45.587+08:00  WARN 1228 — [user-server] [           main] trationDelegate$BeanPostProcessorChecker : Bean ‘org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration’ of type [org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected into a currently created BeanPostProcessor [lbRestClientPostProcessor]? Check the corresponding BeanPostProcessor declaration and its dependencies.

注意看上面的异常信息,有任何跟我项目有关的东西吗?没有吧

那有任何跟依赖冲突有关的东西吗?看上去也没有

那这个异常什么时候才开始有的?Spring整体升级之后

好,那既然这样,我们可以大胆的认为这是一个SpringBoot的bug。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

一顿搜索之后,我在github上找到了这个 issue:https://github.com/spring-cloud/spring-cloud-commons/issues/1315

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

还真是Spring的bug,不过不是SpringBoot,而是SpringCloud的bug。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

这位大佬也说了,将会在下一个版本(2023.0.1)中修复它,预计2月20日(今天是1月28日),不过他们会先发布新的Commons,用以修复这个bug。

在我看到这个issue的时候,新版的 SpringCloudCommons已经发布了:https://spring.io/blog/2024/01/23/spring-cloud-commons-4-1-1-has-been-released

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

于是我对项目中的依赖进行替换,由于这个依赖是从其他Spring-Cloud的组件中自动继承过来的,所以我们只需要在依赖管理里面指定下版本就可以了。

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-commons</artifactId>
      <version>4.1.1</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

添加完之后,果然没有再报warn了,之后等 SpringCloud2023.0.1 发布了,再做一下替换就好了。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

更新自动注入文件

SpringBoot2.7时已经提出使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 代替 spring.factories:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes#changes-to-auto-configuration

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

我升级到 SpringBoot3.2 时,还是支持  spring.factories 的,但再过几个版本可能就不支持了,这边建议直接迁移下,这块几乎没什么成本的。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

源代码

这部分升级的改动有点多,以为已经搞好了,就提PR到main分支了,结果又蹦出来新的问题。

建议需要升级 SpringBoot3.x 的朋友,在看完这篇文章之后,还是再去把官方文档过一遍,看看有没有其他受影响的地方,这样稳妥一点。

代码已提交到GitHub:

  • https://github.com/stick-i/scblogs/pull/200
  • https://github.com/stick-i/scblogs/pull/201
  • https://github.com/stick-i/scblogs/pull/202

源码建议单个 commit 结合 commit message 来查看,这样会更有条理,而不是整个 pr 一起看。

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

最后也附上一些我参考到的官方链接:

  • SpringBoot3.0升级指南:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide
  • 3.2发布记录:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes
  • 3.0发布记录:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes
  • 2.7发布记录:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.7-Release-Notes
  • 3.0配置更新记录:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Configuration-Changelog

后记

本来以为我这小项目简单升级下一两天就弄好了,结果前前后后搞了两周,尤其升级 SpringBoot 的时候,出了一顿问题,踩了不少坑。

看在作者这么认真的份上,建议关注趁早关注下,等我以后火了,在坐的各位就都是老粉了!

踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了


往期推荐

应届生学习Java八个月,offer年薪28W,这一年我经历了什么?

MybatisPlus报错Can not use this method for “getSqlSet” | 带你从源码分析异常

原文始发于微信公众号(程序员阿杆):踩了一堆坑,终于把微服务系统全面升级 JDK17 和 SpringBoot3 了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207018.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!