从JDK8升级到JDK17

一、概述

鉴于 JDK8 已经是老古董,还有性能问题,兼且各个公司已经不再维护 1.8 JDK ,所以升级 公司的核心产品之一 的后端到 JDK 17 是相对要紧的事情。

通过升级到 jdk17 ,具有以下好处:

  • 不要在头疼同时适应两个 jdk 放下适应 JDK8 的负担
  • 在生产环境基本上只需要部署一个 jdk 即可
  • 具有更好的性能
  • 能够利用上更好更新的组件版本。例如 springboot3,spring6.x都是基于jdk17的。
  • 更好的安全性。这对于项目很重要。因为许多客户会安排安全测试,过时的jdk是一个不好解决的问题。升级到jdk17能够更好解决这个问题
  • 更好的竞争能力。当我们的核心jdk是17的时候,毫无疑问比那些还沉滞在jdk8的竞争对手更好,尤其是功能相差不大的情况下。

本次升级后端,大概耗费了一周的时间,其次 httpsecurity 耗费了比较多的时间。

整体上,还算顺利。

二、步骤详情

总体上遵循以下步骤:

(1) 升级准备

(2) 确定 spring 组件版本

spring.io 上看了下,选择 springboot-3.3.0

(3) 确定其它组件版本

(4) 升级有关代码

(5) 调整其它配置

(6) 解决有关异常

(7) 测试

(8) 完成升级

2.1. 升级准备

由于 是一个大的版本升级,所以需要做以下准备:

(1) 确认是否能升级

除了前文提到的原因,还需要确定当前这个产品是否可以升级,毕竟 JDK17 JDK1.8 不一样,且升级了 JDK17 后,有关的组件都要一起升级(是否有相关的版本,相关版本是否稳定 ?)

考虑到 我们的产品 没有使用太多的三方软件,即使有,也都是流行的

现在 jdk17 都已经发布了快 3 年了;有很多其它公司也升级到了 jdk17;部分公司已经把他们的产品升级到JDK21了。

结合这些因素, 产品升级 jdk17 不存在技术障碍!

之所以没有考虑立刻升级到JDK21,是因为其它很多产品都在JDK17,其次一次性到21,没有那么大把握。步子太大,会不会扯蛋了?

虽然官方的文档说springboot3.3.x支持JDK22。但是由于三方组件的存在,导致不敢一次性迈出太大的步子。

(2) git/svn 上开一个分支,或者直接开一个新的仓库 , 不要影响现有的主干代码

2.2. spring 组件版本

spring.io 上可以看到可用的版本,本着使用最新可用版本的原则,选择了 :

  • springboot-3.3.1 (ga)
  • spring-6.1.10 这是 springboot 限定的版本,所以只需要选择 springboot 版本即可

按照 spring惯例 ,当升级的时候,通常相关的组件都是一期升级的,所以总的来说,只要指定 springboot 版本即可。

2.3. 确定其它组件版本

分类

组件

功能描述

旧版本

升级

新版本

说明

数据存取

druid-spring-boot-starter

数据连接和连接池管理

1.2.11

1.2.23

核心组件,必须升级

jdbc驱动

*

jdbc连接

主要看各个厂家,考虑到 jdbc驱动都是比较成熟的,在jdk17中运行,问题应该也不大

消息队列

org.apache.rocketmq/rocketmq-client

amqp

5.1.0

暂时不升级,这个需要较长时间的测试

http请求

httpclient,httpcore

rest请求

4.5.13

5.3.1/5.2.4

原来是:

org.apache.httpcomPONEnts/httpclient

现在是:

org.apache.httpcomponents.client5/httpclient5

http

javax.servlet/javax.servlet-api

servlet

4.0.1

移除

http

jakarta.servlet/jakarta.servlet-api

servlet

6.0.0

新增。用于替代 javax.servlet-api

JSON

fastjson2

JSON

2.0.32

2.0.51

fastjson2bug较多,尽可能升级下

JSON

com.jayway.jsonpath/json-path

JSON路径分析

2.8.0

ORM

mybatis-spring-boot-starter

orm

2.2.2

3.0.3

不升级会导致 mybatis有关bean初始化异常

ORM

pagehelper-spring-boot-starter

分页

1.4.3

2.1.0

mybatis依赖

ORM

jsqlparser

sql解析

4.2

4.7

pageHelper依赖

XML

javax.xml.bind/jaxb-api

XML 解析

2.3.1

暂时不可替代,不可删除,也不需要升级

文档

swagger

文档

从现有版本移除

定时 /调度

quartz

定时 /调度

2.3.2

通用工具

org.apache.commons/common-lang3

3.12.0

3.14.0

通用工具

org.apache.commons/commons-pool2

2.9.0

2.12.0

通用工具

commons-io/commons-io

2.11.0

通用工具

commons-fileupload/commons-fileupload

1.4

存储

minio

8.2.1

编译

maven-compiler-plugin

编译

3.1

3.13.0

注:

  1. 主要考虑到核心组件即可,其它的小组件遇到了再解决。
  2. 有什么版本可用,可以访问 https://mvnrepository.com ,或则各个组件官网(一般是 GitHub), 或者是国内镜像网站,例如阿里的 https://developer.aliyun.com/mvn/search
  3. 部分组件版本必须在升级中调试后才可以确定

2.4. 升级有关代码

当更换了以下组件之后,需要尽快修改代码,修改的原因主要包含:

(1) 配置变更

主要是 spring 升级导致,可能需要修改配置。当然也可能是其它的组件

(2) 包路径变更

(3) 方法不存在

(4) 方法过时

这个需要特别注意 - 如果可能应该尽量把过时的方法移除掉,替换为正常的方法。

2.4.1. 修改 yml 配置

  • 修改范围 -spring.redis 修改为 spring.data.redis-- 这是 spring 要扩大 spring.data 的范围

在spring.data的域名之下,有很多的内容,远不止redis.除了基本的JDBC,还有Rest,elasticsearch,jpd,ldap等等。

  • 添加参数( bean 相关 )

spring:

main:

allow-circular-references: true

allow-bean-definition-overriding: true

spring6.1.10 中,这两个属性默认是 false. 如果你的项目不存在循环引用,或者覆盖定义的情况,那么可以不添加 .

  • 移除配置

移除 swagger 配置 - 这个太垃圾,过分入侵,还浪费了自有的注释,增大程序员的工作量

希望有直接能够利用javaDoc的类似组件。

2.4.2. java 基础类型有关的

基础类型主要指 Integer,Long,BigDecimal,BigInteger 等等。

jdk17 中,许多方法已经被标注为过时 (deprecated)

(1) java 基类 new Class("xxx") 需要修改为 Class.valueOf("xxx")

new Long("xx"),new Integer("xxx"),new Byte("xx"),new Short("")

这些都要修改为对应的 valueOf("xxx")

jdk 这么做,主要是出于性能考虑,尤其针对 Integer

(2) Class.newInstance()

需要把这个替换为 getDeclaredConstructor().newInstance()

(3) Spring.Base64Util 过时,改用 apache Base64

(4) ruoyi 自身的 Base64, 移除掉,避免和 apache 的冲突。

(5) ruoyi 自身的 md5Util 移除

spring 自身的改变来看, spring 也逐渐向 java 标准和阿帕奇基金会靠近,一个是为了标准,其次是避免浪费

时间,最后是不要给 spring 用户带来困扰。 spring 只要做好自己的就行了。

ruoyi如果用于项目还是可以的,但是用于产品开发还是需要进行较多的改造。 因为产品要求更高的安全、适应度、性能等。

2.4.3. servlet 相关

由于在 jdk17 中移除了 javax 的部分包,所以 很多 javax.xxx 都需要修改 jakarta.xxx

这里主要包含:

(1) javax.servlet

(2) javax.annotation

其它 javax.net,javax.sql 等则继续保留着。

2.4.4. httpclient 相关

具体略,总之需要修改。

httpclient5 有重大变更:支持 http2, 异步支持,更好的连接池等

2.4.5. spring-security

这个改变比较大,在 spring6.x 主要通过注解和定义 bean 来实现 spring-security 配置,而在 5.x 中,则是通过扩展 WebSecurityConfigurerAdapter 来实现。

@Configuration

@EnableWebSecurity

@EnableGlobalAuthentication

@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)

public class SecurityConfig {

}

注意,不要去 override 已有的实现,否则配置还是比较麻烦的。

spring 的思路就是你可以改配置,改零件,但是不要改核心。如果要改核心,那么太费劲了。

在这个类中实现以下几个 bean 即可:

  • AuthenticationProvider
  • AuthenticationManager
  • SecurityFilterChain
  • BCryptPasswordEncoder

其中 SecurityFilterChain 是关键,这里主要配置白名单

另外一个变化是,禁用了默认的 logout, 而是新增了一个 /logout 接口:

/**

* 执行退出

* @param request

* @return

* @since 1.5

*/

@PostMapping("/logout")

public AjaxResult logout(HttpServletRequest request) {

try {

LoginUser user=SecurityUtils. getLoginUser ();

String key=CacheConstants. LOGIN_TOKEN_KEY + user.getToken();

redisCache.deleteObject(key);

return AjaxResult. success ();

}

catch (Exception e) {

//如果有异常,则证明已经退出了,不要阻拦

return AjaxResult. success ();

}

}

2.4.6. jsqlparser

主要是因为 pageHelper 升级了。

当然产品 本身也有用到 jsqlparser

2.5. 调整其它配置

主要是编译方面的配置。

由于升级了 jdk ,包括核心组件 maven-compiler-plugin 所以有些原来的默认设置需要进行调整。

2.5.1. 调整编译选项

eclipse 中,其实只需要设置 pom.xml 中配置即可,无需修改工程的环境配置。

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.13.0</version>

<configuration>

<source>${java.version}</source>

<target>${java.version}</target>

<encoding>${project.build.sourceEncoding}</encoding>

< parameters >true</ parameters >

</configuration>

</plugin>

红色部分添加上去,并设置为 true

如果不添加这个,那么 spirng 中很多需要通过反射获取信息的方法可能存在问题。

因为这个选项会让 java .java 编译为 .class 的时候,保留方法的名称,而不是把方法名称随意修改为不认识的名称。

2.6. 解决有关异常

2.6.1. bean 异常

2.6.1.1. 循环引用和覆盖

如前,主要新版本中,有些参数修改了默认值,所以修改如下:

spring:

main:

allow-circular-references: true

allow-bean-definition-overriding: true

2.6.1.2. @Primary 问题

当有多个 Datasource 类型的 Bean ,或者类似其它的,则必须为 Bean 添加 @Primary 的注解,否则回报告异常。

Parameter 0 of method sqlSessionFactory in com.ruoyi.framework.config.db.MyBatisConfig required a single bean, but 3 were found:

触发异常的具体代码如下:

而在以前的版本中不存在这个!

解决方式有两个:

(1) 在参数上简单添加 @Qualifier("masterDataSource") -- 解决了 mybatis, 但是还要解决 quartz 等等。放弃这个方法

(2) 直接修改定义 DataSource 的地方,为主 bean 添加 @Primary ,就用这个✔

2.6.2. jackson 序列化异常

无法处理 key 类型不是 String 类型 , 例如如果是以下类型的 JSON

{

"batchDetail": {

1: "good"

}

}

在序列化的时候会报告异常:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')

必须自定义 HttpMessgeConverter ,以便可以自定义处理这种类型的 key

但这会导致自定义白名单功能错误,所以还需要调整白名单功能

2.6.3. factoryBeanObjectType 异常

经过定位,这是 mybatis 没有升级导致的。

注:一开始的时候,并没有立刻要升级 mybatis ,虽然意识到了,但是并没有那么做。

2.7. 测试

(1) 每个地方都需要测试

(2) 反复测试

这是总的原则。

测试需要持续较长时间,严格而言,需要再考费一个月左右。

从目前来看,总体是可用!

从性能上看,JDK17的程序的确响应更快一些,从页面的响应也可以看出来!

2.8. 完成升级

完成升级后,关闭原来 git 上代码的权限,设置为只读,并通知有关人。

热门手游下载
下载排行榜