spring3.*和jdk8版本冲突问题及解决方案

背景描述

15年的一个老系统,使用的ssh框架,在1.6jdk下运行了五年,最近因部署环境切换,新环境对JDK版本统一升级为1.8版本,在1.8版本jdk下,系统启动报错。

(整个问题排查过程由新环境平台厂商大牛整理,并征求同意后发出来。)

问题概述

先说结论和解决方案:

  • 问题描述:spring3.* 版本不支持JDK8,启动时报错。

  • 问题原因:spring使用asm类库操作Java class文件,spring3.* 依赖的asm类库版本比较老,而JDK8之后Java class格式有变化,老版本的asm类库不能支持导致报错。

  • 解决方式和建议:

    1. 升级spring到spring4以上版本(新应用或老应用大改推荐)

      • spring4开始正式支持JDK8,实际是升级了配套的asm类库到可以支持JDK8的版本

      • 适用于新应用(新应用也强烈不建议使用spring3版本)

      • 对于老应用从spring3升级到spring4/5可能会引发很多兼容性的问题,最好是整个spring生态一起升级到新版本,但这样工作量会比较大,因此只适合老应用大改。

    2. 升级spring到spring3.2.18版本(老应用小改推荐):

      • Spring3.2.18版本是spring3系列最后一个版本

      • 保持了spring3的兼容性,同时asm类库也升级到了可以支持JDK8的版本

      • 可以比较好的平衡:改动量小,能解决spring3和jdk8的冲突问题,又不必让老应用面临升级spring4大版本的风险

    3. 降级JDK到7(老应用完全改不了的最后方案)

      • 不建议采用,JDK7过于陈旧,而且会导致后续没法使用JDK8的特性和支持这些特性的类库

      • 偏离主流技术栈(统一使用JDK8),增加开发运维的复杂度

      • 建议:只有在上面两个方法都无法使用时,不得已而为之的最后备选方案

问题排查

如果对这个问题的细节有兴趣,请细看下面的内容。

问题原始错误信息和输入

JDK 8和spring 3.x不匹配,应用在启动时初始化spring时报错,实际异常如下:

 

问题分析

从异常信息看,是spring在启动初始化时,通过 spring-asm 类库操作Java class文件时报错:

Caused by: java.lang.IllegalArgumentException
    at org.springframework.asm.ClassReader.<init>(Unknown Source)
    at org.springframework.asm.ClassReader.<init>(Unknown Source)
    at org.springframework.asm.ClassReader.<init>(Unknown Source)
    at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:52)
    at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:80)
    at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:101)
    at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:257)
    ... 47 more

Spring官方对这个问题的分析:

IllegalArgumentException initializing an application compiled with Java 8:

社区2013年就报告了这个版本冲突问题,从分析上看是spring使用的 asm 类库版本太低,而JDK8之后Java class格式有变化,老版本的asm类库不能支持处理 Jdk8 的class文件导致报错抛异常。

ASM类库介绍

特别高亮一下 ASM 类库,ASM 是Java社区广泛使用的Java字节码操作和分析框架工具,可以用来修改已有的java class文件或者动态生成java class。

ASM对java 8的支持,始于 2013年10月发布的 asm 5.0 bata 版本:

因此,要解决和JDK8的版本冲突问题,就必须升级spring配套的asm类库到asm5.0版本。

Spring使用ASM类库的方式

Spring重度依赖ASM,但spring使用ASM类库的方式比较特殊,历史上有三次变更:

  1. 直接使用官方ASM(spring1和spring2):

    这是普通使用jar包依赖的方式,在spring 的maven依赖中引入asm,以spring 2 最后一个版本 spring 2.5.6为例:

     <dependency>
                <groupId>asm</groupId>
                <artifactId>asm</artifactId>
                <version>2.2.3</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>asm</groupId>
                <artifactId>asm-commons</artifactId>
                <version>2.2.3</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>asm</groupId>
                <artifactId>asm-util</artifactId>
                <version>2.2.3</version>
                <optional>true</optional>
            </dependency>

    注意 asm.jar 中的 java package 名是 org.objectweb.asm

  2. spring-asm(spring3.0和spring3.1版本)

    Spring3之后没有再按照普通的方式使用asm官方类库,而是做了一次 repackge,将asm的java package 从 org.objectweb.asm 改名为 org.springframework.asm,然后打包并发布为单独的 spring-asm 类库如 spring-asm-3.0.5.RELEASE.jar

    对比如下图:

    在这里可以看到 spring-asm 类库的历史版本记录,从3.0.0版本开始,到3.1.4版本结束:

    https://repo.spring.io/release/org/springframework/spring-asm/

  3. asm in spring-core(spring3.2及之后的版本)

    在spring3.2版本之后,spring修改了repackage asm的方式,package名维持不变,但是不再使用 spring-asm 这样的单独类库,而是把 asm 的内容打包到了 spring-core 中:

    spring3.2系列早期版本repackage 的是 asm 4.0 版本,依然不支持jdk8;但在后期版本(应该是从3.2.14或者3.2.16)开始就repackage了支持 asm 5.0版本。

    从 spring 的 java docs 文档中可以看到:

    https://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/asm/package-summary.html

    Spring's repackaging of org.objectweb.asm 5.0 (for internal use only).

    简单和安全起见,升级到3.2最后一个版本 3.2.18 是可以确认支持jdk8。

问题验证

出现问题的应用,使用的spring版本是 spring 3.0.5 ,配套的spring-asm 3.0.5 对应的asm是3.0版本,不支持jdk8,因此报错。

验证了以下几种解决方案:

  • 降级JDK到7版本,验证通过:使用了Orcale JDK7U80版本,这是JDK7最后一个小版本

  • 升级spring到3.2.18版本,验证通过

参考资料

 

原文地址:https://www.cnblogs.com/rimmy/p/13849225.html