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 版本即使看上去编译没问题, 也可能运行不了.

相关推荐
喜欢流萤吖~1 小时前
微服务架构解析:从单体到分布式
spring boot·后端
小江的记录本1 小时前
【分布式】分布式核心组件——分布式锁:Redis/ZooKeeper/etcd 实现方案(附全方位对比表)、优缺点、Redlock、时钟回拨问题
java·网络·redis·分布式·后端·zookeeper·架构
小江的记录本1 小时前
【分布式】分布式核心组件——分布式ID生成:雪花算法、号段模式、美团Leaf、百度UidGenerator、时钟回拨解决方案
分布式·后端·算法·缓存·性能优化·架构·系统架构
GetcharZp7 小时前
拒绝低效!这款神器,让你的终端效率起飞 | 深度解析 fzf 终极指南
后端
自珍JAVA8 小时前
高效处理Long列表与集合运算:基于RoaringBitmap的工具类解析与应用场景
后端
小码哥_常8 小时前
Spring Boot项目上线秘籍:日志、监控、异常处理全攻略
后端
GreenTea9 小时前
AI 时代,工程师的不可替代性在哪里
前端·人工智能·后端
朦胧之9 小时前
AI 编程开发思维
前端·后端·ai编程
希望永不加班11 小时前
Spring AOP 代理模式:CGLIB 与 JDK 动态代理区别
java·开发语言·后端·spring·代理模式
浮游本尊11 小时前
一次合同同步背后的多阶段流水线:从外部主数据到本地歧义消解
后端