Java版本升级带来的问题
前因
java更新迭代速度巨快无比,Spring Framework 6 等项目已经至少需要 Java 17。但是,对于 Java 版本的采用是相对缓慢的。例如,在 Java 11 发布四年之后(2022年),只有不到 49%的 Java 应用在使用11版本。
但是:例如Spring Framework 出现了性能极高的优化,甚至其他框架也出现了里程碑式优化,那面就升级Java版本带来的优势是大于弊端的
后果
将应用升级至新的 Java 版本,意味着开发人员需要解决 Java 内部变更和功能移除所带来的所有问题。这涉及的功能包括 Nashorn、J2EE 和 Java 等包的移除、API 的变更以及对 Java 内部访问更严格的限制。盲目的升级至新的版本,很有可能造成应用中断等不可控因素。
问道包括但不局限于:
1、删除了一些 API 如 sun.misc.*导致代码出现 ClassNotFoundException。
2、Java Version 的 Schema 发生变化导致原来判断 Java 版本的逻辑出现异常。
3、用户代码中使用了私有的 API,使用了标记为废弃的 API 等。
4、JPMS(Java Platform Module System)的引入导致一些反射代码会无法工作。
5、删除了 J2EE 相关的包。
如果需要升级的应用依赖了成百上千的二方和三方 jar,而这些 jar 可能也存在兼容性问题,更进一步,如果需要升级的应用几十个甚至几百个,那么带来的额外工作量可想而知。
版本升级问题案例
Java8 -> Java11 升级中API的变化如下
DK 11中删除的各个API | |
---|---|
类/方法被删除 | 附加说明/参考 |
java.lang.Runtime.runFinalizersOnExit(boolean) | 危险运行FinalizersOnExit 弃用Java的终结器 |
java.lang.SecurityManager.checkAwtEventQueueAccess() | 安全管理器和Java SE JDK JDK-8177554 JDK-8029886 JDK-8186535 |
java.lang.SecurityManager.checkMemberAccess(java.lang.Class,int) | |
java.lang.SecurityManager.checkSystemClipboardAccess() | |
java.lang.SecurityManager.checkTopLevelWindow(java.lang.Object) | |
java.lang.System.runFinalizersOnExit(boolean) | 危险运行FinalizersOnExit 弃用Java的终结器 |
java.lang.Thread.destroy() | 线程方法destroy()和stop(Throwable)在JDK 11中删除 |
java.lang.Thread.stop(java.lang.Throwable) |
从Java 11中删除模块级API | ||
---|---|---|
名称 | 模块已卸下 | 潜在的第三方更换 |
JavaBeans激活框架 (JAF) | java.activation | Maven神器 |
通用对象请求代理体系结构 (CORBA) | java.corba | 玻璃鱼-科尔巴 |
下表中列出的其他模块的聚合器模块 | java.se.ee | |
Java交易API (JTA) | java.transaction | Maven神器 |
XML绑定的Java体系结构 ( JAXB ) | java.xml.bind | Maven神器 |
XML Web Services的Java API (JAX-WS) | java.xml.ws | Maven神器 |
常用注释 | java.xml.ws.annotation | Maven神器 |
以 java.lang.Thread.stop(java.lang.Throwable)
API 为例,演示版本升级带来危害。
简单的SpringBoot项目
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/testemt")
public String testEmt(){
Thread thread = new Thread();
try{
thread.stop(new Exception());
} catch (UnsupportedOperationException e){
System.out.println( "捕获UnsupportedOperationException " + e);
return "error !";
}
return "success !";
}
}
Java8
在jdk 1.8版本中 ,项目会正常运行没有问题,同时可以捕获异常
升级到Java11
如果升级到 jdk 11 版本 ,首相项目就会报错,因为 11 版本已经没有java.lang.Thread.stop(java.lang.Throwable)
这个方法了。
再次访问方法的既定URL 会报错,应用产生中断报错
后台日志会打印没有 java.lang.Thread.stop(java.lang.Throwable)
方法的error日志
版本升级应对方案
EMT4J 目前支持了从 JDK 8 升级到 JDK 11&17 的分析,后续也会不断的更新对于最新的 LTS 版本的支持。
目前支持通过如下 3 种方式使用:
- Maven插件 可以在开发阶段就发现问题
- 命令行工具 无需启动应用,但可能存在误报
- Java Agent 可以获取项目运行时上下文,获取调用栈,报表信息更准确
EMT4J 官网中详细记录三种使用方案
EMT4J: https://github.com/adoptium/emt4j#use-the-emt4j-agent
下面仅列出Maven插件使用方法
将以下配置添加到 pom.xml(如果是多模块项目.xml根 pom):
<build>
<plugins>
<plugin>
<groupId>org.eclipse.emt4j</groupId>
<artifactId>emt4j-maven-plugin</artifactId>
<version>0.7.0</version>
<executions>
<execution>
<phase>process-test-classes</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<fromVersion>8</fromVersion>
<toVersion>11</toVersion>
<outputFile>report.html</outputFile>
</configuration>
</plugin>
</plugins>
</build>
然后运行以下命令:
$ mvn process-test-classes
EMT4J的报告将在项目目录中生成。
用户也可以直接运行以下命令,无需修改pom.xml:
# 通过默认配置运行
$ mvn process-test-classes org.eclipse.emt4j:emt4j-maven-plugin:0.7.0:check
# 通过 -D 指定文件来执行
$ mvn process-test-classes org.eclipse.emt4j:emt4j-maven-plugin:0.7.0:check -D outputFile=emt4j-report.html -D priority=p1
配置:
- fromVersion:项目当前使用的 JDK 版本。支持 8 和 11,默认支持 8。
- toVersion:目标 JDK 版本。支持 11 和 17,默认为 11。
- outputFile:EMT4J报告的目的地。默认值为报表.html。
- priority:最低规则优先级。支持 P1、P2、P3 和 P4。未设置默认值。
- verbose:如果为 true,则打印更详细的消息。