Gradle Shadow插件依赖运行时无法检测问题求助
问题根源
IDEA里运行客户端正常是因为它直接把所有依赖Jar丢进了系统类路径,Java自带的类加载器能扫描到所有类。但用Shadow打包成胖Jar后,FML用的是自定义隔离类加载器——哪怕你能在Jar里看到第三方类,FML的加载器也只会优先加载你模组包名下的内容,以及通过Forge依赖机制声明的外部库,不会主动读取Jar根目录下的第三方类。
另外,httpasyncclient这类库可能和Minecraft/Forge自带的旧版本类路径冲突,就算你打包进去了,也会被FML加载的旧版本覆盖,导致找不到正确的类。
解决步骤
1. 给第三方依赖做重定位(Relocate)
Shadow的核心作用就是处理这类问题——把第三方类移到你模组的包名下,让FML的加载器认为这是你模组的一部分。修改build.gradle配置:
shadowJar { // 把http相关类重定位到你模组包下的lib子目录 relocate 'org.apache.http', '你的模组包名.lib.org.apache.http' // 顺带重定位httpasyncclient依赖的commons类,避免版本冲突 relocate 'org.apache.commons', '你的模组包名.lib.org.apache.commons' } // 让build任务自动触发shadowJar打包 build.dependsOn shadowJar
把你的模组包名替换成你实际使用的包名(比如net.narnianknight.mymod)。
2. 检查模组依赖声明
不要在@Mod注解或者mcmod.info里把httpasyncclient声明为Forge依赖——FML会优先去官方仓库寻找该依赖,而不是使用你打包进Jar的版本。如果这个库完全是你模组自用的,就不要在模组元数据里声明它。
3. 验证打包结果
执行gradlew shadowJar后,到build/libs目录找到带-all后缀的Jar文件,打开检查类的路径:确认http相关类是在你的模组包名/lib/org/apache/http/下,而不是根目录的org/apache/http/。如果位置不对,说明重定位规则没生效,检查包名拼写是否正确。
4. 处理传递依赖
httpasyncclient依赖httpcore、commons-logging等库,Shadow默认会将这些传递依赖一起打包,但必须给它们也加上重定位规则,否则还是会和Minecraft自带的版本产生冲突。
补充说明
IDEA运行正常的原因是:它的「Minecraft Client」配置会把所有依赖Jar直接添加到系统类路径,Java的系统类加载器不限制类的位置,只要在类路径内就能找到。但FML为了实现模组隔离,限制了类加载范围,只有模组包内的类才会被优先加载,根目录的第三方类会被直接忽略。
内容的提问来源于stack exchange,提问作者Narnianknight

