当前位置: 首页 > news >正文

jdk8升级JDK17避坑指南

jdk8升级JDK17避坑指南

  • jdk8升级JDK17避坑指南
    • 一、模块化对反射的影响
    • 二、删除的内置库
      • 2.1、删除JAXB、soup相关
      • 2.2、删除javax.annotation
      • 2.3、删除sun.misc.* 下的包,如sun.misc.BASE64Encoder
    • 三、字体相关报错
    • 四、jvm参数修改

jdk8升级JDK17避坑指南

随着SpringBoot2.7的发布,支持jdk8~jdk21。Springboot3.X发布,最低需要jdk17。升级jdk17是大势所趋。
本文升级适用于SpringBoot2.7.x从jdk8升级到jdk17操作指南,成功把ruoyi4.x微服务的jdk8升级到jdk17,应用到生产。

参考1:重磅!Spring Boot 2.7 正式发布、
参考2:hutool-希望Hutool能支持下JDK8~JDK17的所有版本
参考3:aliyun-一文详解|从JDK8飞升到JDK17,再到未来的JDK21
参考4:程序员DD-从 Java 8 升级到 Java 17 踩坑全过程,建议收藏!
参考5:老卫waylau-JDK

一、模块化对反射的影响

正式开发业务系统,使用反射修改jdk自带类场景较少。即使修改,通过–add-opens的jvm参数,即可解决该问题。

由于jdk9增加的模块化设计,导致对系统内置类反射受到限制,出现类似的错误,自己开发的类没有影响。

 public static void main(String[] args) {// jdk17使用反射,无需修改即可正常使用的示例//在 Java8 中,没有人能阻止你访问特定的包;只要 setAccessible(true) 就可以了NucPerson nucPerson = new NucPerson();nucPerson.setPersonName("张三");ReflectUtil.setFieldValue(nucPerson, "personPhone", "18800001111");//hutool5中工具类System.out.println(nucPerson.getPersonPhone());System.out.println(ReflectUtil.getFieldMap(NucPerson.class));// jdk17使用反射系统模块的类,必须要增加--add-opens的反射示例// Java9 模块化以后,一切都变了,只能通过 --add-exports 和 --add-opens 来打破模块封装Object stringValue = ReflectUtil.getFieldValue(nucPerson.getPersonName(), "value");System.out.println(stringValue);}
java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.Map sun.reflect.annotation.AnnotationInvocationHandler.memberValues accessible: module java.base does not "opens sun.reflect.annotation" to unnamed module @52d455b8at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) ~[na:na]at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) ~[na:na]at cn.hutool.core.util.ReflectUtil.setAccessible(ReflectUtil.java:966) ~[hutool-core-5.7.19.jar:na]at cn.hutool.core.util.ReflectUtil.getFieldValue(ReflectUtil.java:266) ~[hutool-core-5.7.19.jar:na]at cn.hutool.core.util.ReflectUtil.getFieldValue(ReflectUtil.java:234) ~[hutool-core-5.7.19.jar:na]at cn.hutool.core.annotation.AnnotationUtil.setValue(AnnotationUtil.java:215) ~[hutool-core-5.7.19.jar:na]

解决:增加jvm启动参数(java虚拟机启动参数)

--add-opens java.base/sun.reflect.annotation=ALL-UNNAMED

汇总:

        -Djdk.home=/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home-Xms24m-Xmx768m-DTopSecurityManager.disable=true--add-exports=java.desktop/com.sun.java.swing.plaf.gtk=ALL-UNNAMED--add-exports=java.desktop/sun.awt=ALL-UNNAMED--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor.event=ALL-UNNAMED--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED--add-exports=java.desktop/sun.swing=ALL-UNNAMED--add-exports=jdk.attach/sun.tools.attach=ALL-UNNAMED--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED--add-opens=java.desktop/javax.swing.plaf.synth=ALL-UNNAMED--add-opens=java.base/java.net=ALL-UNNAMED--add-opens=java.base/java.lang.ref=ALL-UNNAMED--add-opens=java.base/java.lang=ALL-UNNAMED--add-opens=java.desktop/javax.swing=ALL-UNNAMED--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED-XX:+IgnoreUnrecognizedVMOptions-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/Users/duandazhi/Library/Application Support/VisualVM/2.1.2/var/log/heapdump.hprof

--add-opens--add-exports区别,opens是深度反射,可以打开对私有变量、成员的反射;exports只能打开公有变量的反射。总结:使用--add-opens肯定没问题。
java虚拟机选项
总结:模块化需要增加的JVM打开反射参数,对常规业务系统影响不大,毕竟对系统类反射操作很少,对自己开发的类进行反射操作并没有限制。

二、删除的内置库

从jdk8~jdk17 有删除部分类,但都有对应的开源平替库,只需要找到对应库的maven依赖即可,因此影响可控。

高版本最坑应该是这一条:
Java 11 :移除JavaEE和CORBA模块

对于Java EE和CORBA模块在Java 9开始就不推荐使用了。而从Java 11开始正式删除了这部分内容,所以当升级到Java 11或更高的版本的话,务必要先更急以下内容相关的代码:
移除的包:

  • javax.xml.ws (JAX-WS)
  • javax.xml.bind (JAXB)
  • javax.xml.soap
  • javax.activation (JAF)
  • javax.validation
  • javax.mail
  • javax.xml.ws.annotation (Common Annotations)
  • java.corba (CORBA)
  • java.transaction (JTA)
  • java.se.ee (以上6个模块的聚合模块)

移除的工具:

  • wsgen and wsimport (from jdk.xml.ws)
  • schemagen and xjc (from jdk.xml.bind)
  • idlj, orbd, servertool, and tnamesrv (from java.corba)

2.1、删除JAXB、soup相关

比如javax.xml.bind.annotation.XmlElement、javax.xml.soap.SOAPElement相关包的类就不存在了,需要手工引入。

我的项目里面就使用到了webservice,即:jaxb(实体类和xml转换)、soapwebservice的http网络请求。
受到影响的hutool工具类cxf相关:JAXBUtilSoapClientJakartaServletUtilMailUtil全部使用jakarta命名;
业务场景,系统请求外部接口使用是webservice,实体类和xml转换需要用JAXBUtil、发送Http请求使用SoapClient

        <!-- 适配jdk升级到9及以上,移除了 javax.xml.bind (JAXB) 的问题; javax需要从外部引入 --><!--JAXB (JSR 222) java.xml.bind,JAXBUtil相关 模块的接口 API,如:import javax.xml.bind.annotation.XmlElement; --><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.9</version></dependency><!-- JAXB (JSR 222) Reference Implementation --><dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.4.0-b180830.0438</version></dependency><!-- 适配jdk升级到9及以上,java.lang.NoClassDefFoundError : javax/xml/soap/SOAPException--><!-- create SAAJ meta-factory: Provider com.sun.xml.internal.messaging.saaj.soap.SAAJMetaFactoryImpl not found--><!-- SoapClient 相关的,如:import javax.xml.soap.SOAPElement;--><dependency><groupId>javax.xml.soap</groupId><artifactId>javax.xml.soap-api</artifactId><version>1.4.0</version></dependency><dependency><groupId>com.sun.xml.messaging.saaj</groupId><artifactId>saaj-impl</artifactId><version>1.5.1</version></dependency>

2.2、删除javax.annotation

比如代码中使用了,代码中用到了 javax.annotation.* 下的包,目前就找不到了。

import javax.annotation.PostConstruct;
import javax.annotation.Resource;@Component
public class RedisLimit {private  DefaultRedisScript<String> redisLUAScript ;@Resourceprivate RedisTemplate<String, Object> redisTemplate;@PostConstructpublic void init(){redisLUAScript = new DefaultRedisScript<>();redisLUAScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limitRate.lua")));redisLUAScript.setResultType(String.class);}
}

解决

<dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version>
</dependency><!-- 或者-->
<!-- https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api -->
<!-- https://mvnrepository.com/artifact/jakarta.annotation/jakarta.annotation-api -->
<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>1.3.5</version>
</dependency>

2.3、删除sun.misc.* 下的包,如sun.misc.BASE64Encoder

1、比如 sun.misc.BASE64Encoder,替换掉这个类。

        //jdk11被删除的库sun.misc.BASE64Encoder base64Encoder = new BASE64Encoder();base64Encoder.encode("xxxx");//使用java.util.Base64中的提供的新库替换java.util.Base64.Encoder encoder = Base64.getEncoder();encoder.encode("xxxx".getBytes());

2、netty 低版本使用了 sun.misc.*,编译错误信息如下
升级最新版本即可解决

 Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.netty.util.internal.PlatformDependent0at io.netty.util.internal.PlatformDependent.getSystemClassLoader(PlatformDependent.java:694) ~[netty-all-4.0.42.Final.jar!/:4.0.42.Final]

3、 lombok 使用了 com.sun.tools.javac.* 下的包,升级到最新版本即可。

三、字体相关报错

项目中使用验证码、图片、excel升级到jdk17报错

java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManagerException java.lang.UnsatisfiedLinkError: /usr/local/jdk-17.0.9+9/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
  1. Centos上解决:sudo yum install freetype-devel -y
  2. jvm设置设置无头模式 :JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true"

四、jvm参数修改

开发可以使用高版本的JDK,字节码级别设置为低版本,就可以避免使用高版本被删除的API。低版本代码(只要是高版本未删除)都可以在高版本直接运行,兼容性可控。
idea设置
适配jdk8、jdk17的的springCloud启动脚本

#!/bin/bash
APP_NAME=nuc-esquery.jar
PROFILE=dev #dev、test、prod、vfic等等
JVM_MODE=prod #test、prod ,启用不同的JVM配置
PID=$(ps -ef|grep $APP_NAME|grep -v grep|awk '{print $2}')# 示例目录:`cd $(dirname .)/..; pwd`、`cd $(dirname .)/.; pwd`、`cd $(dirname $0)/..; pwd`
export BASE_DIR=`cd $(dirname .)/.; pwd`
export CUSTOM_SEARCH_LOCATIONS=file:${BASE_DIR}/conf/#堆最大内存、最小内存、年轻代堆内存、
#老年代不用调,一般会是年轻代的2倍
#元空间一般也不用调整,元空间独立jvm内存,直接在物理内存上, 会自动增加
#输出:oom时候的,堆内存信息,可能:年轻代oom、元空间oom、老年代oom
#输出:gc时候的,gc日志
## 参数:-D:使用 Property形式获取;--:使用Spring里面通用的获取方式
#===========================================================================================
# JVM Configuration
#===========================================================================================
#设置测试、生产环境中,不同的JVM内存配置
if [[ "${JVM_MODE}" == "test" ]]; thenJAVA_OPTS="-Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=640m "JAVA_OPTS="${JAVA_OPTS} -Dmyserver.mode=test "JAVA_OPTS="${JAVA_OPTS} -Dmyserver.name=application_xyz "
else#JAVA_OPTS="-server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m "JAVA_OPTS="-server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m "#JAVA_OPTS="-server -Xms8g -Xmx8g -Xmn4g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m "JAVA_OPTS="${JAVA_OPTS} -Dmyserver.mode=prod "JAVA_OPTS="${JAVA_OPTS} -Dmyserver.name=application_xyz "
fi#设置JDK8、JDK9+不同JDK版本的JVM垃圾回收器的参数
## jdk1.8返回:1;jdk17返回:17
JAVA_MAJOR_VERSION=$(java -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p')
if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; thenJAVA_OPTS="${JAVA_OPTS} -Xlog:gc*:file=gc_trace.log:time,tags:filecount=10,filesize=102400 "#解决:Could not initialize class sun.awt.X11FontManage,设置无头模式 JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true"
elseJAVA_OPTS="${JAVA_OPTS} -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=java_heapdump.hprof -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc_trace.log -verbose:gc -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M "
fi# aliyun arms
## https://arms.console.aliyun.com/#/tracing/agentList/cn-hangzhou?tab=java
#ARMS_JVM="-javaagent:/root/arms/ArmsAgent/arms-bootstrap-1.7.0-SNAPSHOT.jar -Darms.licenseKey=1426648921313446@917412bed1df111 -Darms.appName=hesuan_app146 "
ARMS_JVM=" "# --logging.config:自定义使用外置的logback,方便用户修改,不用重新打包
# --spring.config.additional-location:可以覆盖默认的配置,覆盖默认的加载顺序和优先级。application.properties.example
#JAVA_OPTS="${JAVA_OPTS} --logging.config=./conf/project-logback.xml"
#JAVA_OPTS="${JAVA_OPTS} --spring.config.additional-location=${CUSTOM_SEARCH_LOCATIONS}"if [ "$PID" != "" ]; thenecho '================>停止服务..........'kill -9 $PIDsleep 3s
fiecho '================>nohup.out日志清空成功.........'
echo '' > nohup.out
echo '================>开始重启服务.........'#启动参数
### 获取自定义启动参数的方式
### @Value("${ghouse.datacenterId:0}")
### private long datacenterId;
### 项目经常部署,开发、测试、生产、生产备用等多套环境,nacos相关会经常变动
START_ARGS="--spring.profiles.active=${PROFILE}\--custom=127.0.0.1:8850\--evn=my-test-key\--ghouse.datacenterId=1\--spring.cloud.nacos.discovery.server-addr=10.16.58.146:8850\--spring.cloud.nacos.config.server-addr=10.16.58.146:8850\--spring.cloud.inetutils.preferred-networks=10.16\--spring.cloud.nacos.config.namespace=xahs-dev\--spring.cloud.nacos.discovery.namespace=xahs-dev
"echo ''
java -version
echo ''## 常规启动版本
## java [options] -jar <jar 文件> [args...]
#nohup java $JAVA_OPTS -jar vaccine-epi.jar --spring.profiles.active=prod >> nohup.out 2>&1 &
#nohup java $JAVA_OPTS -jar $APP_NAME 2>&1 &
echo "java $ARMS_JVM $JAVA_OPTS -jar $APP_NAME $START_ARGS >> nohup.out 2>&1 &"
nohup java $ARMS_JVM $JAVA_OPTS -jar $APP_NAME $START_ARGS >> nohup.out 2>&1 &tail -f nohup.out

相关文章:

  • 大创项目推荐 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv
  • 本地映射测试环境域名,解决登录测试环境后,也可以使用本地域名访问,可以正常跑本地项目
  • k8s之陈述式资源管理
  • 八、typescript 高级类型与模块
  • 【每日一题】LeetCode206.反转链表
  • Python圣诞树代码
  • 【UML】第12篇 序列图(1/2)——基本概念和构成
  • [数据结构]树与二叉树的性质
  • ------- 计算机网络基础
  • 思福迪运维安全管理系统 test_qrcode_b RCE漏洞复现
  • 【FPGA】Verilog 实践:优先级编码器 | Priority encoder
  • 一个实用的Wrapper类,解决mfc使用sqlite3时的中文乱码问题
  • W5500-EVB-Pico评估版介绍
  • 二、C#基础语法( 异常处理)
  • 使用JAVA Zookeeper构建分布式键值存储
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【Under-the-hood-ReactJS-Part0】React源码解读
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 230. Kth Smallest Element in a BST
  • Fundebug计费标准解释:事件数是如何定义的?
  • JavaScript 一些 DOM 的知识点
  • Linux CTF 逆向入门
  • React as a UI Runtime(五、列表)
  • React系列之 Redux 架构模式
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • Vim 折腾记
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  •  一套莫尔斯电报听写、翻译系统
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​决定德拉瓦州地区版图的关键历史事件
  • #LLM入门|Prompt#3.3_存储_Memory
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .net core Swagger 过滤部分Api
  • .net 开发怎么实现前后端分离_前后端分离:分离式开发和一体式发布
  • .pyc文件还原.py文件_Python什么情况下会生成pyc文件?
  • [@Controller]4 详解@ModelAttribute
  • [04] Android逐帧动画(一)
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • [BIZ] - 1.金融交易系统特点
  • [C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [cb]UIGrid+UIStretch的自适应
  • [C语言]——C语言常见概念(1)
  • [HDOJ4911]Inversion
  • [IE 技巧] 显示/隐藏IE 的菜单/工具栏
  • [JavaScript]_[初级]_[不使用JQuery原生Ajax提交表单文件并监听进度]
  • [leetcode]_Symmetric Tree
  • [Linux] CE知识随笔含Ansible、防火墙、VIM、其他服务