从JDK8升级到JDK17

jdk8,jdk17 · 浏览次数 : 3

小编点评

一、概述 鉴于JDK8已经是老古董,还有性能问题,兼且各个公司已经不再维护1.8的JDK,所以升级公司的核心产品之一的后端到JDK到17是相对要紧的事情。通过升级到jdk17,具有以下好处: 1. 不需要在头疼同时适应两个jdk,放下适应JDK8的负担 2. 生产环境基本上只需要部署一个jdk即可 3. 具有更好的性能能够利用上更好更新的组件版本,例如springboot3, spring6.x都是基于jdk17的。 4. 更好的安全性。这对于项目很重要。因为许多客户会安排安全测试,过时的jdk是一个不好解决的问题。 5. 升级到jdk17能够更好解决这个问题更好的竞争能力。当我们的核心jdk是17的时候,毫无疑问比那些还沉滞在jdk8的竞争对手更好,尤其是功能相差不大的情况下。 本次升级后端,大概耗费了一周的时间,其次httpsecurity耗费了比较多的时间。整体上,还算顺利。 二、步骤详情 总体上遵循以下步骤: 1. 升级准备 - 确认是否能升级 - 在git/svn上开一个分支,或者直接开一个新的仓库,不要影响现有的主干代码 2. 确定spring组件版本 - 在spring.io上查看可用的版本 - 选择springboot-3.3.0和其它需要的组件版本 3. 升级有关代码 - 修改yml配置 - 修改java基础类型 - 修改servlet相关 - 修改httpclient相关 - 修改spring-security - 修改jsqlparser - 调整其他配置 4. 解决有关异常 - 解决bean异常 - 解决jackson序列化异常 - 解决factoryBeanObjectType异常 5. 测试 - 每个地方都需要测试 - 反复测试 6. 完成升级 - 关闭原来git上代码的权限,设置为只读,并通知有关人。

正文

一、概述

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

通过升级到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) 确认是否能升级

除了前文提到的原因,还需要确定当前这个产品是否可以升级,毕竟JDK17JDK1.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过时,改用apacheBase64

(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上代码的权限,设置为只读,并通知有关人。

 

与从JDK8升级到JDK17相似的内容:

从JDK8升级到JDK17

一、概述 鉴于JDK8已经是老古董,还有性能问题,兼且各个公司已经不再维护1.8的JDK,所以升级公司的核心产品之一的后端到JDK到17是相对要紧的事情。 通过升级到jdk17,具有以下好处: 不要在头疼同时适应两个jdk,放下适应JDK8的负担 在生产环境基本上只需要部署一个jdk即可 具有更好的

JDK8升级JDK11最全实践干货来了

截至目前(2023年),Java8发布至今已有9年,2018年9月25日,Oracle发布了Java11,这是Java8之后的首个LTS版本。那么从JDK8到JDK11,到底带来了哪些特性呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK8升级JDK11最全实践。

轻量灵动: 革新轻量级服务开发

从 JDK 8 升级到 JDK 17 可以让你的应用程序受益于新的功能、性能改进和安全增强。下面是一些 JDK 8 升级到 JDK 17 的最佳实战

带你认识JDK8中超nice的Native Memory Tracking

摘要:从 OpenJDK8 起有了一个很 nice 的虚拟机内部功能: Native Memory Tracking (NMT)。 本文分享自华为云社区《Native Memory Tracking 详解(1):基础介绍》,作者:毕昇小助手。 0.引言 我们经常会好奇,我启动了一个 JVM,他到底会

JDK8到JDK17有哪些吸引人的新特性?

本文主要介绍JDK8到JDk17近200个JEP中比较有价值的新特性(按照价值从高到低排序),这里有一部分特性作者也在线上环境使用过,也会将其中的使用心得分享给大家。

Java CompletableFuture 异步超时实现探索

JDK 8 是一次重大的版本升级,新增了非常多的特性,其中之一便是 CompletableFuture。自此从 JDK 层面真正意义上的支持了基于事件的异步编程范式,弥补了 Future 的缺陷。 在我们的日常优化中,最常用手段便是多线程并行执行。这时候就会涉及到 CompletableFuture 的使用。

从DDPM到DDIM (一) 极大似然估计与证据下界

从DDPM到DDIM (一) 极大似然估计与证据下界 现在网络上关于DDPM和DDIM的讲解有很多,但无论什么样的讲解,都不如自己推到一遍来的痛快。笔者希望就这篇文章,从头到尾对扩散模型做一次完整的推导。本文的很多部分都参考了 Calvin Luo[1] 和 Stanley Chan[2] 写的经典

从Mybatis-Plus开始认识SerializedLambda

从Mybatis-Plus开始认识SerializedLambda 背景 对于使用过Mybatis-Plus的Java开发者来说,肯定对以下代码不陌生: @TableName("t_user") @Data public class User { private String id; private

从基础到高级应用,详解用Python实现容器化和微服务架构

本文分享自华为云社区《Python微服务与容器化实践详解【从基础到高级应用】》,作者: 柠檬味拥抱。 Python中的容器化和微服务架构实践 在现代软件开发中,容器化和微服务架构已经成为主流。容器化技术使得应用程序可以在任何环境中一致运行,而微服务架构通过将应用拆分成多个独立的服务,从而提升了系统的

从DDPM到DDIM

现在网络上关于DDPM和DDIM的讲解有很多,但无论什么样的讲解,都不如自己推到一边来的痛快。笔者希望就这篇文章,从头到尾对扩散模型DDPM及其加速方法DDIM做一次完整的推导。