Java 8 到 Java 11 项目迁移指南:如何轻松应对升级挑战
源丨低并发编程(ID:dibingfa)

作者丨山克
老板要求我将一个项目从Java 8迁移到Java 11。我该怎么办?
最简单的方法当然是直接升级。如果遇到一个错误,您将纠正另一个错误。不要看低它,但是对于一个小型且非核心项目来说足够了。
当然,对于更重要的项目和大量的代码行,最标准的方法是面对正式文档,即Java 11迁移描述文档。
它详细介绍了Java 8和Java 11之间可能发生的兼容性问题,并提供了修改措施或建议。
从理论上讲,如果您读字官方文档,并用肉眼扫描代码的每一行,则可以对其进行修改,以便对其进行修改,绝对可以完全迁移。
但是毕竟,人类不是机器。他们编写的代码可能具有数千甚至数万行,并且必须包括引入的第三方库。这显然不是人类所做的事情。
因此,有一个神奇的工具可以帮助我们自动扫描在JDK升级过程中需要修改或注意的区域,并直接生成视觉HTML报告。太神奇了!
这是什么
该工具的名称是EMT4J,它是Java的Eclipse迁移工具包。它被翻译为Eclipse Foundation的字面翻译,用于迁移,工具集和为Java准备的。
官方网站地址是:
官方网站介绍了它非常友好。我直接与汤姆(Tom)担任主角一起想出了一个短篇小说。它可能说汤姆正准备将项目从8移至11,这非常痛苦。使用EMT4J后,我感到非常高兴。
我非常喜欢这种方法。如果您有兴趣,可以阅读。没有困难的话。
该工具由阿里巴巴撰写并捐赠给Eclipse Foundation,因此阿里巴巴云的官方官方帐户发起了有关介绍它的推文
但是,官方网站和推文包含很多宣传和许多混乱的信息。作为一个可爱的开发人员,我仍然更喜欢阅读简单而粗糙的代码存储库:
只有一个屏幕截图包含核心功能,下载地址以及快速访问开发人员的三个最重要点。
如何使用它
单击下载地址下载以下载EMT4J后,检查其目录结构并发现它非常简单,清晰和直观。
➜ Downloads tree emt4j-0.3
emt4j-0.3
├── bin
│ ├── analysis.bat
│ └── analysis.sh
└── lib
├── agent
│ ├── emt4j-agent-jdk11-0.3.jar
│ └── emt4j-agent-jdk8-0.3.jar
├── analysis
│ ├── asm-9.2.jar
│ ├── commons-io-2.4.jar
│ ├── commons-lang3-3.8.jar
│ ├── emt4j-analysis-0.3.jar
│ ├── emt4j-common-0.3.jar
│ ├── gson-2.9.0.jar
│ ├── lombok-1.18.8.jar
│ ├── mvel2-2.4.12.Final.jar
│ ├── slf4j-api-1.7.30.jar
│ └── velocity-engine-core-2.3.jar
└── maven-plugin
└── emt4j-maven-plugin-0.3.jar
如果您仅使用Javaagent方法来分析项目,那么对于用户,您只需要注意代理目录中的两个罐子即可。
从8个迁移到其他JDKS的用法:
emt4j-agent-jdk8-0.3.jar
从11到另一个JDK迁移:
emt4j-agent-jdk11-0.3.jar
我们编写一个简单的Hello World程序,并使用JDK8编译。
public class Hello {
public static void main( String[] args ) {
System.out.println( "Hello World!" );
}
}
然后,进行8至11的迁移分析。
java -javaagent:emt4j-agent-jdk8-0.3.jar=to=11 Hello
将输出结果可视化成HTML格式。
sh analysis.sh -o report.html emt4j-XXX.dat
打开此HTML文件并发现输出结果正是我们预期的。
是的,自然而然的世界计划自然不需要考虑迁移的兼容性〜
以一个复杂的项目为例
我们试图分析一个更复杂的项目,并继续执行这些步骤。这次报告输出很有趣。
我们可以看到在目录中的摘要将该项目从8到11迁移时需要考虑的所有问题。例如,JDK 9 Java的模式已更改,单击。
在如何修复它中,给出了官方文档,我们将继续单击它。
简而言之,Java.version系统变量的字符串格式输出已更改。如果您的程序依靠此字符串进行拦截和判断,则需要小心。
例如,在报告中问题的上下文中,给出了所有可能影响的方法,让我们举一个例子。
在著名的log4j中著名的AbstractStringlayout类中,有一种ISPREJAVA8方法用于确定以前是否是Java 8的版本。
// log4j-core:2.10.0
private static boolean isPreJava8() {
String version = System.getProperty("java.version");
String[] parts = version.split("\\.");
try {
int major = Integer.parseInt(parts[1]);
return major < 8;
} catch (Exception var3) {
return true;
}
}
如果您使用Java 11运行此方法,则将获得真实,这意味着11是8之前的版本。这显然是错误的。您可以说出一个简单的调试是什么问题。
当然,我使用的版本是log4j核:2.10.0。我相信著名的Log4J项目必须在以后的版本中解决此问题。
果然,这个问题是在提交上特别解决的。
维修方法也很低,这意味着要判断第一点是1,然后以新的方式做出判断,即使用该点背后的数字作为主要版本。
好吧,似乎将该项目从8升级到11的最安全方法是同时升级Log4J三方依赖的旧版本。
什么是原则
让我们探讨下面的该项目的原则。 Github的官方网站上给出了建筑图。
左侧是运行项目的代理分析,上面是对静态jar和类文件等的分析。当简短解析符号时,它们是不同的。
中间部分是符号解析完成后,需要读取规则规则。这些规则是确定是否存在兼容性问题的逻辑。如果他们匹配,将记录他们。
右下角是输出刚刚以更友好的方式记录的兼容性问题,例如HTML。
因此,整个架构仍然非常清楚,重点是如何处理细节,我只会在这里谈论关键链接。
代理方法分析可能不是很直观。如果您想了解,则可以从此标准Javaagent项目的首要方法开始。
在这里,我碰巧了解了另一种直接扫描类文件或JAR文件进行静态扫描的方法,这非常直观。
首先,我们故意编写了可以触发java的代码。上面提到的Version格式兼容性问题。
class Hello {
public static void main(String[] var0) {
System.out.println("hello");
System.out.println(System.getProperty("java.version"));
}
}
然后将其编译到类文件中,并将其作为扫描文件夹放入ClassFiles文件夹中。
然后执行SH命令,表明您检查文件夹中的风险项目是否迁移至11。
sh analysis.sh -f 8 -t 11 -o report.html classFiles
该SH脚本实际上是执行分析并将上述参数传递到ARG中的主要方法。
// org/eclipse/emt4j/analysis/AnalysisMain.java
public class AnalysisMain {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException, URISyntaxException {
...
ReportConfig config = doAnalysis(args, ...);
doReport(config, ...);
}
...
}
因此,从这个入口开始是一件好事。
主方法的结构也很简单。 DoAnalysis分析了这些类文件并绘制结果。 Doreport将可视化分析结果,例如生成HTML文件。
我不会一一扩展特定的呼叫链,我只会查看非常关键的链接。首先,ClassAnalyzer中的ProcessClass方法使用ASM将类文件解析为各种符号,并将其保存在ClassSymbol对象中。
// org/eclipse/emt4j/analysis/analyzer/ClassAnalyzer.java
protected static void processClass(byte[] classFileContent ...) {
ClassSymbol symbol = ClassInspectorInstance.getInstance().getSymbolInClass(classFileContent);
...
}
然后,各种规则文件使用这些符号信息来做出判断,例如holeclassrule,这意味着需要整个类文件信息来做出判断。
@RuleImpl(type = "whole-class")
public class WholeClassRule extends ExecutableRule {
...
@Override
protected CheckResult check(Dependency dependency) {
Map mvelMap = new HashMap<>();
mvelMap.put("typeSet", dependency.getClassSymbol().getTypeSet());
mvelMap.put("methodSet", toMethodIdentifierSet(dependency.getClassSymbol().getCallMethodSet()));
mvelMap.put("cpSet", dependency.getClassSymbol().getConstantPoolSet());
Object result = MVEL.eval(mvel2Rule, mvelMap);
if (result instanceof Boolean) {
return ((Boolean) result) ? CheckResult.FAIL : CheckResult.PASS;
} else {
throw new JdkMigrationException("Mvel2 rule file" + mvel2RuleFile + " must return a boolean result!Now result type is : " + result.getClass());
}
}
}
在这里,我们刚刚删除符号信息,并将其放入MvelMap中,即排版类型集合,MethodSet方法集合和CPSET CONST CONST池集合。
接下来,使用第三方班级库Mvel进行判断。这是一个工具类,可以使用表达式来做出匹配的判断。这很方便。规则表达式写在此mvel2rule中。
有许多mvel2rule,其中要检查Java的兼容性问题的表达式写在以下文件中。
// emt4j-common/src/main/resources/default/rule/8to11/data/mvel2-rule-getjavaversion.cfg
methodSet.contains('java.lang.System.getProperty') &&
(
cpSet.contains('java.version') ||
cpSet.contains('java.specification.version') ||
cpSet.contains('java.runtime.version')
)
此规则表达式很容易理解,您可以理解它而不理解其写作方法,只需使用它
system.getProperty
方法,有一个
java.version或java.specification.version或java.runtime.version
当它被认为是兼容性问题时。
可以看出,这个判断非常粗糙,只是一个简单的字符串包含判断。可以看出,兼容性问题的判断无法逃脱此方法。不要认为使用任何智能分析方法。
当记录此兼容性问题并最终输出HTML文件时,将通过ResourceBundle查看官方文档说明,详细信息将写入HTML文件中。
最后,我看到了效果,这很简单。
再说几句话
通过体验和理解这个项目,我们可以学到很多东西。尽管麻雀很小,但它具有所有内部器官。
首先,它是通过代理和SH提供给用户的,但是中间的分析和判断逻辑是共享的。
然后,该项目使用专门解析类文件的ASM工具,提取符号信息以供以后使用。
MVEL也被用作规则判断的工具,因此规则判断只需要书写表达方式。
当最终输出为HTML时,为了查询相应兼容性问题的官方描述,ResourceBundle用于查找官方文档描述。因此,我们可以继续更深入地了解官方文档的查询规格,这是从代码级别进行的,并且非常严格。
一个好的工具是使用如此多的知名工具使自己方便。然后,我们还可以使用这个项目来做自己的事情。
例如,您制作的工具甚至是您自定义的JDK需要检查业务代码中是否存在兼容性问题,或者您只想扫描业务代码中是否有任何关心的内容,那么您就可以修改各种该项目中的规则表达式并自定义您自己的HTML报告格式以完善此功能。
其他事情,如何扫描班级,如何解析班级符号,如何将规则判决来判断以及如何输出和渲染HTML,您不必自己做。
1。
2。
3。
4。
5。