Java8升级到Java11的实践
# 为什么升级?Java 8不好用了么
升级Java版本主要有两方面考虑:
- 一个是高版本提供了更丰富的便利性、更高的性能、更全面的安全性。详情见:Java历代版本新特性 9-11部分
- 一个是Spring Boot 3.x开始,只支持Java 17+了
# 为何不直接升级到Java 17?
为何不直接升级到Java 17,因为风险太大。比如Java 15移除了Nashorn JavaScript脚本引擎。
还有就是一些主流的框架暂时还未完全支持Java 17,比如dubbo。
# 如何升级?
首先,我们需要注意到已经删除的功能,包括:
- JavaFX。从Java 11开始,该平台不再包含JavaFX作为规范的一部分,并且大多数JDK构建都将其删除。您可以使用来自 Gluon 的单独 JavaFX 构建,也可以将 OpenJFX 依赖项添加到您的项目中。
- 字体。曾几何时,JDK包含一些字体,但从Java 11开始,它们被删除了。例如,如果您使用Apache POI(用于Microsoft Office兼容文档的Java API),您将需要字体。操作系统需要提供字体,因为它们不再存在于 JDK 中。但是,在 Alpine Linux 等操作系统上,必须使用该 apt install fontconfig 命令手动安装字体。根据您使用的字体,可能需要额外的包。
- Java Mission Control。Java Mission Control 曾经包含在 JDK 中,但现在它可以以新名称作为单独的下载提供:JDK Mission Control。
- CORBA模块。Java的CORBA模块没有正式的替代品,该模块在Java 11中被删除。但是,Oracle GlassFish Server 包含 CORBA 的实现。
其次,要了解Java 9到Java 11 之间的版本新特性,可以参考上面的文章。
个人认为重中之重是Java 9模块系统和JVM统一日志。不了解模块系统,就会对反射API的各种错误抓狂;不了解统一JVM日志,就分析不了GC的状况。
# 模块系统
关于模块系统,参考:Java 模块系统 (opens new window)
这里补充一下--illegal-access
参数,因为模块化的关系,如果模块中的软件包未导出或打开,但你仍然要使用这些软件包,可能会面临破坏应用程序的风险。如果在运行期反射调用内部API,可以通过添加--illegal-access=${value}
来检查,它有四个值:
permit
: 未来可能会移除。仅在第一次反射调用内部api的时候报警warn
:每次次反射调用内部api的时候报警debug
:在warn的基础上,加上堆栈输出deny
: 拒绝所有非法反射访问内部api
要注意Java 17中--illegal-access
的默认值为deny,且不允许修改。
所以使用Java 11的时候有两种选择:
- 使用默认值,不做任何配置。那么只会在第一次反射内部API的时候报警
- 为了以后更方便的升级Java 17,对于某些模块,手动打开开放的开关。比如JVM启动参数中增加
--illegal-access=deny --add-opens java.base/java.lang.invoke=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED
# 统一JVM日志
参考此篇文章:Java 9 平台和JVM日志 (opens new window)
项目线上配置:
-Xlog:gc*,safepoint:/data/logs/jvm/app-gc.log:time,uptime:filecount=2,filesize=200M
# 软件包的升级
三方包的Java 11兼容也是一个大问题,这里仅对我遇到的情况做一个记录。
# 1、javaassist包,CtClass.toClass()调用方式的变化
CtClass类实例生成的调用方式从.toClass(classloader, neighborClass.getProtectionDomain())
变为.toClass(neigborClass)
,可以参考:issues 369 (opens new window)
# 2、Security.removeProvider
引发的javax.net.ssl.SSLProtocolException: Cannot decode named group: x25519
这个错误实在令人费解,最后在Java 11 Migration (opens new window) 中找到了答案,搜索到三方包中有这么一行代码:Security.removeProvider("SunEC");
,非常无语。
解决方案是升级三方包。
# 3、MethodHandler API在不同版本的表现
项目中主要是反射调用接口的default方法,在不同的Java版本中,调用方式也不同。
参考:反射调用默认方法在Java 8/9/10中的不同变现 (opens new window)
# JVM参数变化
可以在官网上查看9-11的JVM参数变化:
- Java 9 Deprecated Java Options (opens new window)
- Java 10 Deprecated Java Options (opens new window)
- Java 11 Deprecated Java Options (opens new window)
不过这样并不是高效的做法,你可以直接通过java -XX:+PrintCommandLineFlags ... -version
来检测原先的JVM参数是否可用,或者通过这个网站来检测:JaCoLine (opens new window)
# 总结
我们分析了Java版本升级的必要性,对于版本升级过程中的主要步骤和事项进行了说明,希望对你有用!
祝你变得更强!