同一JVM中能否使用同一库的两个版本?Forge模组依赖问题求助
Forge 1.16.5模组集成JDA依赖加载问题解决方案
问题描述
开发基于Minecraft Forge 1.16.5的模组时,集成JDA(Java Discord API)库遇到以下问题:
- 已将JDA及其所有依赖打包进模组Jar,但服务器运行时抛出
NoClassDefFoundError,找不到gnu.trove.map.TLongObjectMap类 - 反编译确认Jar内包含完整的JDA及依赖,但服务器
libraries文件夹存在该依赖的旧版本,推测Forge优先加载了旧版本 - 尝试过ForgeGradle的Jar-in-jar功能,但连JDA本身都无法被找到
- IntelliJ本地运行模组无此错误
报错信息
[14okt2023 23:58:10.203] [Server thread/ERROR] [net.minecraft.server.MinecraftServer/]: Encountered an unexpected exception java.lang.NoClassDefFoundError: gnu/trove/map/TLongObjectMap at net.dv8tion.jda.internal.utils.Checks.notBlank(Checks.java:98) ~[discord_statusbot:13.0] at net.dv8tion.jda.api.entities.Activity.playing(Activity.java:128) ~[discord_statusbot:13.0] at mopsy.productions.discord.statusbot.BotManger.regBot(BotManger.java:24) ~[discord_statusbot:13.0] at mopsy.productions.discord.statusbot.StatusbotMain.start(StatusbotMain.java:45) ~[discord_statusbot:13.0] at net.minecraftforge.eventbus.ASMEventHandler_0_StatusbotMain_start_FMLServerStartedEvent.invoke(.dynamic) ~[?:?] at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) ~[eventbus-4.0.0.jar:?] at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) ~[eventbus-4.0.0.jar:?] at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) ~[eventbus-4.0.0.jar:?] at net.minecraftforge.fml.server.ServerLifecycleHooks.handleServerStarted(ServerLifecycleHooks.java:106) ~[forge:?] at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:622) ~[?:?] at net.minecraft.server.MinecraftServer.func_240783_a_(MinecraftServer.java:232) ~[?:1.8.0_311] at java.lang.Thread.run(Unknown Source) [?:1.8.0_311] Caused by: java.lang.ClassNotFoundException: gnu.trove.map.TLongObjectMap at java.lang.ClassLoader.findClass(Unknown Source) ~[?:1.8.0_311] at java.lang.ClassLoader.loadClass(Unknown Source) ~[?:1.8.0_311] at cpw.mods.modlauncher.TransformingClassLoader.loadClass(TransformingClassLoader.java:94) ~[modlauncher-8.1.3.jar:?] at java.lang.ClassLoader.loadClass(Unknown Source) ~[?:1.8.0_311] ... 12 more
解决方案
1. 正确配置ForgeGradle的Jar-in-jar功能
之前的Jar-in-jar尝试失败大概率是配置错误,按以下步骤调整:
- 在
build.gradle中配置Jar任务,嵌入依赖并排除签名文件:jar { from configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA' } - 配置依赖时排除服务器中已存在的旧版本,重新引入兼容版本:
dependencies { implementation 'net.dv8tion:JDA:4.4.0_350' // 替换为你使用的JDA版本 implementation('net.dv8tion:JDA:4.4.0_350') { exclude group: 'gnu.trove', module: 'trove4j' } implementation('gnu.trove:trove4j:3.0.3') { transitive = false } } - 创建
src/main/resources/discord_statusbot_at.cfg文件,声明优先加载的内嵌依赖路径:net/dv8tion/jda/** gnu/trove/** - 在Jar任务中添加manifest属性,告知Forge加载内嵌依赖:
jar { manifest { attributes([ 'FMLAT': 'discord_statusbot_at.cfg' ]) } }
2. 使用ShadowJar重命名依赖包(彻底解决冲突)
如果Jar-in-jar仍无法解决,用ShadowJar将JDA及依赖的包名重命名,避免与服务器旧版本冲突:
- 引入ShadowJar插件:
plugins { id 'com.github.johnrengelman.shadow' version '7.1.2' } - 配置重定位规则,修改依赖包的包路径:
shadowJar { relocate 'gnu.trove', 'mopsy.productions.discord.shaded.gnu.trove' relocate 'net.dv8tion.jda', 'mopsy.productions.discord.shaded.net.dv8tion.jda' mergeServiceFiles() } build.dependsOn shadowJar - 代码中引用JDA时,使用重命名后的包路径(ShadowJar会自动修改字节码中的引用,也可保持原路径,但推荐显式使用新路径)。
3. 临时移除服务器旧版本依赖(仅测试用)
测试环境中可暂时删除服务器libraries文件夹中的旧版本trove4j Jar,验证是否为版本冲突导致。但生产环境不推荐此操作,可能影响其他模组运行。
关于同一JVM中使用同一库的两个版本
同一JVM默认无法直接加载同一全限定类名的两个版本,类加载器会优先加载第一个找到的类。但可通过两种方式实现共存:
- 自定义类加载器:为不同版本的库创建独立类加载器,但在Forge模组中使用需谨慎,可能与Forge自身类加载体系冲突。
- 包重命名:即上述ShadowJar方案,修改依赖包的包名,让两个版本的类成为不同全限定名的类,这是Forge模组中处理依赖冲突最可靠的方式。
内容的提问来源于stack exchange,提问作者Mopsy
相关产品推荐
相关产品推荐

