最近公司进行JDK升级,从6到8。各位看官莫笑,传统企业大多如此,使用JDK1.4及以下的也不在少数。

升级内容主要分几大类:

  1. 框架:首当其冲就是Spring,从Spring4开始完整支持JDK8。其实说到根本也是字节码操作版本的问题,包括annonation的扫描、代理类生成、lamda表达式的应用等。
  2. 字节码操作类,包括asm、AspectJ等,也需要升级到对应版本支持JDK8。
  3. 其他工具类,以Hibernate、QueryDSL等ORM框架位为主,因为它们也不可避免的需要解析和生成Class。

以上基本能覆盖绝大多数情况,剩下的就要通过自动构建、部署和测试发现了。

果不其然,通过CI发现部分依赖Drools的工程在测试的时候有规则执行问题。排查后,发现Drools5.6.0.Final依赖的mvel2版本问题,需要升级到2.2.0+才可以。可是事情往往不会这么简单,升级了mvel2,发现Spring初始化失败了,在drools相关bean初始化、进行类加载的时候提示IncompatibleClassChangeError。仔细研究下,判断是mvel2升级后,二者类字节码不匹配导致的。一步步debug,找到drools-core中的MethodComparator$Tracer类implememts了mvel2的ClassVisitor;但是,在新版本中ClassVisitor已经变成了Abstract Class!!!

接下来有2个方案;升级到drools6.x;重新编译替换使用ClassVisitor的相关类。

考虑到drools6.x的使用方式变化太大,带来的改动成本太高,于是决定使用后一种方案。

先在drools-core中查找ClassVisitor的实现类,找到还有另一个类也有类似情况。然后对这两个类单独编译后在jar中替换。

完成后,重新运行测试案例。情况又有变化,在一处调用MethodVisitor实例方法的地方又提示IncompatibleClassChangeError,不过具体的message是“org.mvel2.asm.MethodVisitor is class but expect interface”。纳闷…

实在找不到头绪,于是决定看看Class字节码。终于发现此处该调用类的字节码指令是invokeinterface!而MethodVisitor同样在升级后变为抽象类,那JVM执行时就会报告类似错误。

后续将drools-core和drools-compiler的全部源码修改后重新编译,问题解决。

总结几点:

  1. JDK升级还是从有字节码操作的地方下手,提前判断范围
  2. 如果工程依赖类从interface变为class、或者反之,最好是全部重新编译,否则运行时该字节码执行处会引发异常、造成更大的损失

然后,默默拿起《深入Java虚拟机》…

PS:关于JDK升级,后续还有其他相关内容,敬请关注。