0x01.异常定位
当我们升级JDK后在启动spring-boot项目时可能会遇到,org.springframework.core.NestedIOException ASM ClassReader failed to parse class file xxx.class 这个问题,我们查看异常抛出的地方,就能知道实际上是我们在使用字节流加载文件时判断版本号出现了异常,最终可以定位到 Java Error java.lang.IllegalArgumentException: Unsupported class file major version 61 这个异常信息。
我们来看下异常抛出的地方,可以看到是读取了文件的一个 short 整型与 Opcodes.V15 进行了对比,如果大于这个V15则抛出异常
ClassReader(
final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) {
this.classFileBuffer = classFileBuffer;
this.b = classFileBuffer;
// Check the class' major_version. This field is after the magic and minor_version fields, which
// use 4 and 2 bytes respectively.
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V15) {
throw new IllegalArgumentException(
"Unsupported class file major version " + readShort(classFileOffset + 6));
}
// ...后面的省略...
}
0x02.异常分析
通过Opcodes.V15我们可以知道,原来字节码文件也是有一个 major version 的概念,
什么是 java class files major version
我们都知道当一个Java源文件被编译会生成一个class文件,java生成class文件时,java的主次版本号都将确定下来,并分配给生成的class文件,称之为class文件的字节码版本。
在jvm上运行这个class文件是,class文件的主版本号必须在当前java runtime enviroment所支持的版本号之内
每次java发布时都会有一个主版本号,你可以在如下这张表中知道每次java版本发行时的字节码版本。
| Java SE | Major | Supported majors |
|---|---|---|
| 1.0.2 | 45 | 45 |
| 8 | 52 | 45 .. 52 |
| 9 | 53 | 45 .. 53 |
| 10 | 54 | 45 .. 54 |
| 11 | 55 | 45 .. 55 |
| 12 | 56 | 45 .. 56 |
| 13 | 57 | 45 .. 57 |
| 14 | 58 | 45 .. 58 |
| 15 | 59 | 45 .. 59 |
| 16 | 60 | 45 .. 60 |
| 17 | 61 | 45 .. 61 |
| 18 | 62 | 45 .. 62 |
| 19 | 63 | 45 .. 63 |
| 20 | 64 | 45 .. 64 |
| 21 | 65 | 45 .. 65 |
| 22 | 66 | 45 .. 66 |
Reference:ByteCode Version
可以通过如下命令查看我们class文件的字节码版本
javap -v com.github.demo.SpringBootUMApplication | grep "major version"
0x03.解决问题
遇到此类问题可以通过检查自己class文件的版本号和升级JDK或升级其他组件来解决这个问题,如上述这个问题的出现,在我使用了jdk17编译好字节码后,启动springboot时发现在spring-core这个依赖中 org.springframework.asm.ClassReader 检查了字节码文件的版本号,导致应用无法启动。
逛了一圈github发现老哥在2020年时就已经支持了 Major Version 61,再根据老哥这个提交的版本发现,已经在spring5.3.3支持了,那将spring升级到5.3.3就好了