Kotlin 踩坑 - 为什么组件版本切来切去还是提示NoSuchMethodError

背景

A 组件依赖了 jackson-module-kotlin 2.16.0 版本组件. 项目依赖了 A 组件, 但因为 A 组件的 jackson-module-kotlin 因为兼容问题, 没法用太高的版本, 导致 jackson-module-kotlin 需要降级. 因此在项目里面修改了的 jackson 的组件版本, 降成了2.10. 然后就出现下面的现象.

java 复制代码
Caused by:java.Lang.NoSuchMethodError Create breakpoint com.fasterxml.jackson.module.kotlin.KotlinModule.<init>(IZZZLcom/fasterxml/jackson/module/kotlin/SingletonSupport;
ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
at com.pinco.plugin.common.serlallzer.ob]ectMapperFactory$companion.buildob]ectMapper(ob]ectMapperFactory.kt:34)
at com.pinco.plugin.common.serializer.ObjectMapperFactory$Companion.accesssbuildobjectMapper(objectMapperFactory.kt:20)
at com.pinco.plugin.common.serializer.objectMapperFactory.<clinit>(objectMapperFactory.kt:21)
at ardServiceImpl.kt:121)

看了一下代码, 报错的地方在这里, 查看源码没发现有编译的问题.

java 复制代码
val objectMapper = ObjectMapper()
    .registerModule(
        KotlinModule() // 这里抛出异常
    )

初步猜想

是 jackson 2.10 版本, 对kotlin还是不友好? 因为项目的 kotlin 版本是 1.3, 怀疑是因为和 kotlin 版本不兼容导致的. 因为 jackson-module-kotlin 是 jackson 的其中一个模块, 版本要一起更新才不会有兼容问题. 因此在项目里面把 jackson 降到2.5, 还是有这个问题, 低于 2.5 版本的 kotlin 版本太高, 用 2.16 jackson 提示 kotlin版本太高, 怎么改都有异常. 最后某次无意间在 A组件 改了 jackson 的版本, 改成 2.10, 竟然没报错了. 但在 A 组件改jackson版本实际上对项目不会有影响的, 因为 A组件已经强制指定了 jackson 版本. 那为什么改了 A组件的版本就没问题的呢?

分析异常原因

根源还是在异常提示那里, noSuchMethodError的方法有点奇怪, KotlinModule 看调用是一个无参的构造方法, 为什么会提示有一堆的参数呢? (IZZZLcom/fasterxml/jackson/module/kotlin/SingletonSupport; ZZZILkotlin/jvm/internal/DefaultConstructorMarker;). 这是因为 KotlinModule 并非有无参构造函数, 而是默认参数隐藏了入参.

A组件 分别用2.16.0和 2.10.0 打包, 反编译, 发现两个版本打包出来的KotlinModule() 是不一样的.

java 复制代码
  // 2.10.0 版本
  ObjectMapper objectMapper = (new ObjectMapper()).registerModule(
      (Module)(new KotlinModule(0, false, false, 7, (DefaultConstructorMarker)null))
  );
  
  // 2.16.0 版本
  ObjectMapper objectMapper = (new ObjectMapper()).registerModule(
      (Module)(new KotlinModule(0, false, false, false, (SingletonSupport)null, false, false, false, 255, (DefaultConstructorMarker)null))
  );
   

虽然看上去没有编译错误, 但实际依赖 jackson 2.16.0 的 A组件上在运行过程会new 11个参数的构造方法. 而项目里面依赖的是 2.10.0, 只有 5个参数的构造方法, 因此会提示找不到 11个参数的构造方法.

结论

这其实是 kotlin 对默认参数的实现导致的. kotlin 底层是字节码, 字节码层面并没有提供对默认参数的实现, 因此kotlin 只能通过编译来支持新特性.

KotlinModule 因为用了默认参数, 会导致不同版本打包出来的字节码会不一样, 因此会导致兼容的问题. 例如 组件A使用了 2.16.0 打包, 并使用了默认方法 项目就只要能用 jackson-module-kotlin 2.16.0, 改了 jackson 版本即使看上去编译没问题, 也可能运行不了.

相关推荐
xcya3 分钟前
Java ReentrantLock 核心用法
后端
用户4665370150516 分钟前
如何在 IntelliJ IDEA 中可视化压缩提交到生产分支
后端·github
小楓120122 分钟前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
天天摸鱼的java工程师24 分钟前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试
白仑色25 分钟前
Spring Boot 全局异常处理
java·spring boot·后端·全局异常处理·统一返回格式
之诺31 分钟前
MySQL通信过程字符集转换
后端·mysql
喵手32 分钟前
反射机制:你真的了解它的“能力”吗?
java·后端·java ee
用户4665370150533 分钟前
git代码压缩合并
后端·github
武大打工仔37 分钟前
从零开始手搓一个MVC框架
后端
开心猴爷42 分钟前
移动端网页调试实战 Cookie 丢失问题的排查与优化
后端