EMT4J——让 Java 应用升级更轻松

300 阅读6分钟

前言

JDK 升级对于 Java 应用来说是不得不面对的事情,一方面 Java 生态系统希望 Java 应用能跟上最新 JDK 版本:

  • Oracle 建议将 JDK 的 LTS 版本的发布周期从 3 年调整为 2 年,对于只使用 LTS 版本的应用来说,可以在更短时间内使用最新的技术,但这也意味着版本升级会更加频繁。
  • Spring Framework 6 只支持 JDK 17。Spring 是大多数企业级应用依赖的基础框架,意味着不得不升级到 JDK 17。

但是另一方面,现实中的 Java 应用的 JDK 升级较为缓慢。JDK 11 从 2018 年发布近 4 年,已升级 JDK 11 仍未达到半数(2022-state-of-java-ecosystem ) 。而相比之下,JDK 8 发布之后 1 年内可达到 38%,两年达到 64.3%,到 2018 年时近 80%(java-8-adoption-march-2016、jvm-ecosystem-report-2018)。

那为什么升级 JDK 11 积极性不高呢?

可能的原因是多方面的。例如 JDK 8 在语言层面引入了 Lambda 对开发人员非常有吸引力,而 JDK 11 在语言层面的更新显得可有可无。但是有一点是非常明显的,相比从其它版本升级到 JDK 8(JDK 6/7 升级到 JDK 8 对于应用基本无感 ), 升级到 JDK 11/17 难度会大很多,可能会遇到很多兼容性问题,如:

1、删除了一些 API 如 sun.misc.*导致代码出现 ClassNotFoundException。

2、Java Version 的 Schema 发生变化导致原来判断 Java 版本的逻辑出现异常。

3、用户代码中使用了私有的 API,使用了标记为废弃的 API 等。

4、JPMS(Java Platform Module System ) 的引入导致一些反射代码会无法工作。

5、删除了 J2EE 相关的包。

如果需要升级的应用依赖了成百上千的二方和三方 jar,而这些 jar 可能也存在兼容性问题,更进一步,如果需要升级的应用几十个甚至几百个,那么带来的额外工作量可想而知。

正是由于上述的困难,阿里巴巴内部将这些升级的经验通过工具沉淀下来,希望能够通过工具帮忙更多的 Java 应用更高效的升级最新版本 JDK。

EMT4J 简介

目前 Eclipse Migration Toolkit for Java(简称“ EMT4J ” ) 已经在 Eclipse 社区 开源,并 通过  Eclipse Adoptium PMC 评审,作为 Eclipse Adoptium 子项目进行孵化。 阿里云 也是 Eclipse Adoptium 工作组的战略基石成员,参与 Eclipse Adoptium 社区治理,为 Java  Ecosystem 提供完全兼容的、基于 OpenJDK 的高质量 JDK 发行版。

EMT4J 目前支持了从 JDK 8 升级到 JDK 11&17 的分析,后续也会不断的更新对于最新的 LTS 版本的支持。

目前支持通过如下 3 种方式使用:

  • Java Agent
  • 命令行工具
  • Maven插件

EMT4J 架构图如下:

EMT4J —— 工 具使用( 场景演示 )

以一个常见的场景来说明工具的使用:

开发人员张三接到任务,需要将所在团队负责的 8 个 Java 应用(app-service-1 到app-service-8)从 JDK 8 升级到 JDK 17,那张三在 EMT4J 的帮助下如何升级呢?

具体升级操作我们分以下七步走:

1、张三下载了 EMT4J 工具到本地,并且在  /home/jdk17 本地安装了目标版本的 JDK 17。

2、将 app-service-1app-service-8 的应用包下载到 EMT4J 所在机器,放在目录 /home/app/deploy,并将 app-service-1app-service-8 的 JVM 选项放入到  .cfg 的文本文件,放在目录  /home/app/vmoptions

3、运行工具检查: sh ${EMT4J_HOME}/bin/analysis.sh -f 8 -t 17 -j /home/jdk17 /home/app ,其中 -f 8 -t 17 表示从 8 升级到 17, -j /home/jdk17 表示目标版本 JDK 的安装目录,  /home/app 表示需要检查的应用包以及参数文件。

命令执行完成以后,默认会在当前目录生成 report.html。打开检查报告report.html 查看到问题的列表如下:

4、张三点击" The schema of java version changed in JDK9 " 查看问题的详情,看到提示如下具体的类: Location: file:/home/app/deploy/app-service-1/notify-utils-2.2.5.jar, Target: com.app.services1.utils.VersionInfo 。

5、张三打开 app-service-1 的工程看到如下的代码,下面的代码显然是无法处理类似于 17.0.1 版本号。

private final String JAVA_VERSION = System.getProperty("java.version");   
private final int getJavaVersionAsInt() {
    if (this.JAVA_VERSION == null)
        return 0; 
    String str = this.JAVA_VERSION.substring(0, 1);
    str = str + this.JAVA_VERSION.substring(2, 3);
    if (this.JAVA_VERSION.length() >= 5) {
        str = str + this.JAVA_VERSION.substring(4, 5);
    } else {
        str = str + "0";
    } 
    return Integer.parseInt(str);
}

6、张三参考报告中的 How to fix 了解到 JDK 9 以后 Java Version 的 Schema 发生了变化,按新的 Schema 修改代码。

7、张三按依次参考报告中对其它问题修改。修改完成以后,在开发机器上使用目标版本 JDK 启动验证功能正确性。

EMT4J—— 工具特性一览

以上使用  EMT4J 工具 帮助张三从  JDK 8 成功地 升级到 JDK 17,那么  EMT4J 工具具有哪些特性?  

1、支持 Java Agent、命令行工具以及 Maven 插件等方式使用

  • Java Agent 可以获取更多运行时上下文信息,能提供准确调用栈,能发现更多的问题
  • 命令行工具方便使用,无须启动应用,但是可能会存在误报
  • Maven 插件可以集成在构建阶段,可在开发阶段发现问题

2、支持多种潜在不兼容性问题分析

  • JDK 8 到 JDK 11
  • JDK Internal API 的使用
  • System ClassLoader 不再是 URLClassLoader 子类
  • Arrays.asList 返回类型变化
  • Java Version 的 Shema 发生变更
  • JPMS 需要增加 add-exports 和 add-opens
  • 时区数据变更为 CLDR 引起相关不兼容
  • Pattern.compile 的 API 变更
  • JVM 选项变更
  • ...
  • JDK 11 到 JDK 17

  • 删除了 Nashorn

  • 删除了RMI 和 Java Applet

  • 部分 Class 无法反射获取 Field

  • ...

3、支持 HTML、TXT 和 JSON 格式的输出

如何参与贡献?

如果您在使用 EMT4J 的过程中,遇到如下的问题:

  • 工具应该发现的兼容性问题,但是实际没有发现
  • 工具报告的问题上下文不准确,不方便查找问题在哪里
  • 工具报告的问题描述不清晰,难以指导如何修改
  • 增加一些新的功能,比如工具支持其它一些格式
  • ...

都可以通过如下的方式参与贡献:

  • 创建 issue 描述您遇到的问题
  • 自己 Fork 以后修改测试后,并通过 PR 合入 master

结语: 因为 目前还有很多的 Java 应用仍然停留在 JDK 8上,所以这些应用正在规划或者正在升级 JDK 中,希望通过 EMT4J 让我们的升级工作更轻松一些。

我们在工具中沉淀了阿里巴巴升级 JDK 的经验,但是肯定还有很多我们没有遇到的兼容性问题,另外 JDK 新的版本不断会发布,也会不断有新的兼容性问题出来。我们希望通过开源的方式,能够有更多的开发者参与到工具中,给工具不断增强功能,让更多的 Java 应用升级变得更简单。

更多内容 (如何使用、贡献等) 可以参考 (或点击阅读原文直达) : 

github.com/adoptium/em…

— — 完 ——