EMT4J定制规则版:Java 8→17迁移兼容性检测与规则优化实战

文章目录

  • EMT4J使用说明
    • 使用说明
    • 规则修改
      • 去掉以下规则
      • 全部规则
        • [8-11 规则:](#8-11 规则:)
          • [1. JDK internal API (JDK内部API)](#1. JDK internal API (JDK内部API))
          • [2. System classloader not instance of URLClassLoader](#2. System classloader not instance of URLClassLoader)
          • [3. Arrays.asList().toArray return type changed](#3. Arrays.asList().toArray return type changed)
          • [4. Get java version](#4. Get java version)
          • [5. JPMS require add-exports](#5. JPMS require add-exports)
          • [6. JPMS require add-opens](#6. JPMS require add-opens)
          • [7. DateFormat incompatible with CLDR](#7. DateFormat incompatible with CLDR)
          • [8. NumberFormat incompatible with CLDR](#8. NumberFormat incompatible with CLDR)
          • [9. Calendar#getFirstDayOfWeek incompatible with CLDR](#getFirstDayOfWeek incompatible with CLDR)
          • [10. java.util.regex.Pattern.compile flags check](#10. java.util.regex.Pattern.compile flags check)
          • [11. JVM Option not compatible](#11. JVM Option not compatible)
          • [12. Remove CORBA](#12. Remove CORBA)
          • [13. Removed API](#13. Removed API)
          • [14. Incompatible Jar](#14. Incompatible Jar)
          • [15. Deprecated API](#15. Deprecated API)
        • [11-17 规则:](#11-17 规则:)
          • [1. Remove Nashorn](#1. Remove Nashorn)
          • [2. Deprecate the Applet API for Removal](#2. Deprecate the Applet API for Removal)
          • [3. Remove RMI Activation](#3. Remove RMI Activation)
          • [4. Cannot get security class's field](#4. Cannot get security class's field)
          • [5. JVM Option not compatible](#5. JVM Option not compatible)
          • [6. Incompatible Jar](#6. Incompatible Jar)
          • [7. Deprecated API](#7. Deprecated API)

EMT4J使用说明

EMT4J 是一个 Java 版本迁移兼容性检测工具,主要用于识别代码或依赖库在 Java 8→1111→17 升级过程中可能遇到的不兼容问题。它通过预定义规则(如 API 废弃、模块系统限制、JVM 参数变化等)扫描代码或 JAR 包,快速定位潜在风险,帮助开发者提前修复问题,确保迁移后应用正常运行。

  • 如下使用EMT4J扫描Jar包后的报告
  • 分类目录

使用说明

使用该工具检测之前,建议先把项目中的JDK版本、SpringBoot相关依赖及第三方依赖升级到对应的版本再使用该工具。如果直接使用该工具,会检测出很多低版本依赖的问题。
以下拿 https://github.com/adoptium/emt4j/tree/v0.8.0 版本来改造,因为 master 分支还是个 SNAPSHOT 版本,不是很稳定

1、使用插件的方式运行

  • 将以下配置添加到 pom.xml 文件(如果是多模块项目,则添加到根 pom.xml 文件):
xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.eclipse.emt4j</groupId>
            <artifactId>emt4j-maven-plugin</artifactId>
            <version>0.8.0-rules-internal</version>
            <executions>
                <execution>
                    <phase>process-test-classes</phase>
                    <goals>
                        <goal>check</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <fromVersion>8</fromVersion>
                <toVersion>17</toVersion>
                <outputFile>report.html</outputFile>
                <!--指定扫描的jar包-->
                <files>target/xxx.jar</files>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 然后运行以下命令:
bat 复制代码
mvn process-test-classes
  • EMT4J 的报告将在项目目录中生成。

2、直接在项目目录下运行以下命令,无需修改 pom.xml 文件:

前提条件,需要使用对应目标的JDK版本来执行,如执行的是8-17,需要使用17来执行该命令

bat 复制代码
# 以默认配置运行,默认检测的版本范围是 JDK 8 → JDK 11
mvn process-test-classes org.eclipse.emt4j:emt4j-maven-plugin:0.8.0-rules-internal:check
# 指定JDK8-17范围
mvn process-test-classes org.eclipse.emt4j:emt4j-maven-plugin:0.8.0-rules-internal:check -DfromVersion=8 -DtoVersion=17
bat 复制代码
# 通过 `-D` 指定输出文件和优先级
mvn process-test-classes org.eclipse.emt4j:emt4j-maven-plugin:0.8.0-rules-internal:check -DoutputFile=xxx-report.html -Dpriority=p1

规则修改

  • 规则文件在:emt4j\emt4j-common\src\main\resources\default\rule
  • 对应 xml 扫描规则配置:
  • 如果去掉某个规则,直接注释对应的规则,有的规则不只是注释 <rule/>,还需要在对应的 cfg 文件中去掉包路径才不会扫描这个规则

去掉以下规则

1、从JDK 9,java version的schema发生了变化

bash 复制代码
从JDK 9开始,Java版本号的格式发生了变化:

旧格式(JDK 8及之前):1.x(例如 1.8.0_381)
新格式(JDK 9+):MAJOR.MINOR.SECURITY.PATCH(例如 9.0.1, 11.0.4, 17)

如:
位置: file:/D:/emt4j-0.8.0/admin/xxx/target/xxx.jar!/BOOT-INF/lib/byte-buddy-1.10.22.jar!/net/bytebuddy/ClassFileVersion$VersionLocator$ForLegacyVm.class, 目标: net.bytebuddy.ClassFileVersion$VersionLocator$ForLegacyVm

位于 byte-buddy-1.10.22.jar
问题:这个类使用了旧的版本号格式(1.x)来判断Java版本,在JDK 9+上会失效。
例如:if (System.getProperty("java.version").startsWith("1.8"))
→ 在JDK 9+中,java.version 返回 9.0.1,所以条件永远不成立。

大多都是一些三方包会被扫描到

2、JDK 11中时区数据为CLDR,java.text.DateFormat的使用默认的style进行format时在JDK 8和JDK 11输出不一致

java 复制代码
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT); 
System.out.println(df.format(new Date())); 
JDK 8的输出: Jan 14, 2021 11:17:37 AM ; JDK 11的输出: Jan 14, 2021, 11:16:45 AM 区别在于年份后面,对于JDK 8是空格,但是对于JDK 11是逗号.
JDK 8 JDK 11
Jan 14, 2021 11:17:37 AM Jan 14, 2021, 11:16:45 AM
年份后是空格 年份后是逗号
bash 复制代码
如:位置: file:/D:/emt4j-0.8.0/admin/xxx/target/xxx.jar!/BOOT-INF/lib/liquibase-core-4.8.0.jar!/liquibase/dbdoc/HTMLWriter.class, 目标: liquibase.dbdoc.HTMLWriter

修复:
1. 增加 "-Djava.locale.providers=COMPAT"到java选项,从而将默认时区与JDK 8的时区数据保持兼容 
2. 在使用DateFormat的时候避免使用默认的locale,而是显式指定Locale.

3、java.util.Calendar.getFirstDayOfWeek对于某些输入返回值在JDK 8和JDK 11不一致

java 复制代码
Locale locale1 = Locale.forLanguageTag("nl"); 
System.out.println(new GregorianCalendar(locale1).getFirstDayOfWeek()); 
JDK 8的输出是2,但是JDK 11的输出是1
JDK 8 JDK 11
Locale.forLanguageTag("nl") → 星期一(返回 2) Locale.forLanguageTag("nl") → 星期日(返回 1)
荷兰语默认使用星期一为一周起始 CLDR 标准下荷兰语默认使用星期日
bash 复制代码
JDK 11 采用 CLDR 时区数据,而 JDK 8 用旧版数据。Locale 未指定区域时,JDK 11 会按 CLDR 规则处理。

位置: file:/D:/emt4j-0.8.0/admin/xxx/target/xxx.jar!/BOOT-INF/lib/logback-core-1.2.13.jar!/ch/qos/logback/core/util/TimeUtil.class, 目标: ch.qos.logback.core.util.TimeUtil

修复:
1. 增加 "-Djava.locale.providers=COMPAT"到java选项,从而将默认时区与JDK 8的时区数据保持兼容 
2. 在上面的场景中实例化Locale时,同时提供语言和区域,而不仅仅只提供语言

4、代码使用了JDK中标记为Deprecated的API

去掉了8-11、11-17中针对过期API的规则。这些只是被弃用了,还没有被删掉,扫描出来的太多了,大多都是一些低版本的三方包,把对应的三方包升级下就行

bash 复制代码
Deprecated的API后续可能会删除,存在潜在不兼容的风险

修复:参考标记为deprecated的API javadoc的说明替换为相应建议的API

全部规则

8-11 规则:
1. JDK internal API (JDK内部API)
  • 作用 :检测使用 sun.* 等JDK内部API的情况
  • 优先级:p4
  • 说明:JDK 9+ 移除了内部API,使用会导致兼容性问题
2. System classloader not instance of URLClassLoader
  • 作用 :检测系统类加载器是否仍是 URLClassLoader 的子类
  • 优先级:p1
  • 说明 :JDK 9+ 系统类加载器不再是 URLClassLoader 的子类
3. Arrays.asList().toArray return type changed
  • 作用 :检测 Arrays.asList().toArray() 返回类型变化
  • 优先级:p1
  • 说明 :JDK 9+ 中返回类型从 Object[] 变为特定类型数组
4. Get java version
  • 作用:检测获取Java版本的代码
  • 优先级:p1
  • 说明:JDK 9+ 中Java版本获取方式有变化
5. JPMS require add-exports
  • 作用 :检测需要添加 add-exports 的模块系统代码
  • 优先级:p3
  • 说明:JDK 9+ 引入模块系统,需要显式导出包
6. JPMS require add-opens
  • 作用 :检测需要添加 add-opens 的模块系统代码
  • 优先级:p3
  • 说明:JDK 9+ 引入模块系统,需要显式打开包
7. DateFormat incompatible with CLDR
  • 作用 :检测 DateFormat 在CLDR区域设置下的不兼容问题
  • 优先级:p3
  • 说明:JDK 11 使用 CLDR 时区数据,与 JDK 8 不兼容
8. NumberFormat incompatible with CLDR
  • 作用 :检测 NumberFormat 在CLDR区域设置下的不兼容问题
  • 优先级:p3
  • 说明:JDK 11 使用 CLDR 时区数据,与 JDK 8 不兼容
9. Calendar#getFirstDayOfWeek incompatible with CLDR
  • 作用 :检测 Calendar#getFirstDayOfWeek 在CLDR区域设置下的不兼容问题
  • 优先级:p3
  • 说明:JDK 11 使用 CLDR 时区数据,与 JDK 8 不兼容
10. java.util.regex.Pattern.compile flags check
  • 作用 :检测 Pattern.compile() 在JDK 11中添加的标志检查
  • 优先级:p1
  • 说明 :JDK 11 中 Pattern.compile() 添加了标志检查
11. JVM Option not compatible
  • 作用:检测不兼容的JVM选项
  • 优先级:p1
  • 说明:JDK 8 和 JDK 11 的JVM选项有变化
12. Remove CORBA
  • 作用:检测使用CORBA的代码
  • 优先级:p4
  • 说明:CORBA在JDK 11中已被移除
13. Removed API
  • 作用:检测已移除的API
  • 优先级:p1
  • 说明:JDK 11中移除了部分API
14. Incompatible Jar
  • 作用:检测不兼容的JAR包
  • 优先级:p1
  • 说明:某些JAR包在JDK 11中不兼容
15. Deprecated API
  • 作用:检测已废弃的API
  • 优先级:p4
  • 说明:JDK 11中废弃了部分API
11-17 规则:
1. Remove Nashorn
  • 作用:检测使用Nashorn的代码
  • 优先级:p3
  • 说明:Nashorn在JDK 11中已移除,在JDK 17中完全移除
2. Deprecate the Applet API for Removal
  • 作用:检测使用Applet API的代码
  • 优先级:p4
  • 说明:Applet API在JDK 11中已废弃,在JDK 17中将被移除
3. Remove RMI Activation
  • 作用:检测使用RMI Activation的代码
  • 优先级:p4
  • 说明:RMI Activation在JDK 11中已被移除
4. Cannot get security class's field
  • 作用:检测无法获取安全类字段的代码
  • 优先级:p1
  • 说明:JDK 11+ 中安全类的字段访问被限制
5. JVM Option not compatible
  • 作用:检测不兼容的JVM选项
  • 优先级:p1
  • 说明:JDK 11 和 JDK 17 的JVM选项有变化
6. Incompatible Jar
  • 作用:检测不兼容的JAR包
  • 优先级:p1
  • 说明:某些JAR包在JDK 17中不兼容
7. Deprecated API
  • 作用:检测已废弃的API
  • 优先级:p4
  • 说明:JDK 17中废弃了部分API
相关推荐
一点★2 小时前
Java中的常量池和字符串常量池
java·开发语言
咬人喵喵2 小时前
14 类圣诞核心 SVG 交互方案拆解(附案例 + 资源)
开发语言·前端·javascript
开始了码2 小时前
深入理解回调函数:从概念到 Qt 实战
开发语言·qt
问君能有几多愁~2 小时前
C++ 日志实现
java·前端·c++
菜鸟plus+2 小时前
Java 接口的演变
java·开发语言
李慕婉学姐2 小时前
【开题答辩过程】以《基于springboot的地铁综合服务管理系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·spring boot·后端
IT空门:门主2 小时前
Spring AI的教程,持续更新......
java·人工智能·spring·spring ai
期待のcode2 小时前
Springboot配置属性绑定
java·spring boot·后端
JANGHIGH3 小时前
c++ 多线程(二)
开发语言·c++