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

相关推荐
星辰徐哥3 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥3 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约3 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee4 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐4 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs4 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐4 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司4 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
码农阿豪4 小时前
从零到一:Spring Boot快速接入金仓数据库实战
数据库·spring boot·后端
追逐时光者4 小时前
一个基于 .NET 与 Avalonia 构建、面向 TrinityCore 的开源 WoW 数据库编辑器
后端·.net