Gradle testScoverage运行MINA SSHD2.8.0报NoClassDefFoundError
根因分析
你遇到的NoClassDefFoundError并不是sshd-common包缺失导致的,报错信息里的Could not initialize class XXX是核心判断依据:这类报错代表对应类的class文件实际存在,只是JVM第一次加载该类、执行静态代码块/静态变量初始化逻辑时抛出了未捕获的异常,后续所有访问该类的请求都会直接抛出这个错误,和类完全不存在的报错性质完全不同。
两个报错对应的具体触发原因如下:
org.apache.sshd.common.util.logging.LoggingUtils初始化失败:SSHD 2.8.0版本的该类在静态初始化逻辑中调用了SLF4J 1.7.25版本才新增的API,如果你项目中因为依赖冲突引入了低于该版本的slf4j-api,首次调用时会抛出NoSuchMethodError,直接导致类初始化失败。你当前列出的依赖中只引入了scala-logging,没有显式声明slf4j-api的版本,很容易被其他传递依赖拉入低版本SLF4J。org.apache.sshd.common.SshConstants$LazyMessagesMapHolder初始化失败:该静态内部类初始化时会通过Java SPI机制加载SSH消息名称映射器,需要读取sshd-common jar包内META-INF/services/路径下的SPI配置文件,如果你的构建流程(比如资源过滤规则、胖包合并逻辑)把该文件过滤掉或者覆盖了,初始化时读不到映射实现就会抛出异常,导致类加载失败。
修复步骤
- 执行
./gradlew dependencies > dep_tree.txt导出全项目依赖树,搜索所有slf4j-api的引用,排除所有版本低于1.7.25的slf4j-api传递依赖,在根项目构建配置中强制统一slf4j-api版本为1.7.36(SSHD 2.8.0适配的稳定版本),注意不要引入SLF4J 2.x版本,SSHD 2.8.0未适配SLF4J 2.x的API。 - 检查Gradle的
processResources任务配置,如果你配置了文件include/exclude过滤规则,确认没有排除META-INF/services/**路径下的文件。如果使用了Shadow插件、Spring Boot打包插件这类构建可执行胖包的工具,将SPI文件的合并规则修改为追加模式,不要使用“最后引入的文件覆盖之前文件”的默认逻辑,避免不同依赖的SPI配置互相覆盖丢失。 - 如果需要定位最原始的初始化异常,可以在测试启动参数中添加JVM参数
-XX:+TraceClassLoading,第一次触发报错类加载时,控制台会打印导致初始化失败的原始异常栈,直接对准原始异常修复即可,无需反复猜测问题点。
内容的提问来源于stack exchange,提问作者yi1
相关产品推荐
相关产品推荐

