SBT可运行Scala项目,但直接调用Jar报ClassNotFoundException
嗨,这个问题我之前也碰到过,本质就是SBT默认打包的Jar和scala命令的类路径逻辑不匹配:
为什么sbt run能正常工作?
当你执行sbt run时,SBT在背后悄悄做了很多事:它会自动收集项目的所有依赖(比如你用到的scala-graph库),把这些依赖的Jar都加入到运行时的类路径里,所以JVM能找到scalax.collection.GraphBase$InnerNode这个依赖类,程序自然能顺利跑起来。
为什么直接用scala命令跑Jar会报错?
SBT默认用sbt package生成的Jar是个瘦Jar(thin jar)——它只包含你自己写的代码,完全不打包任何第三方依赖的代码。当你直接用scala target/scala-2.12/coloring_2.12-0.1.jar运行时,JVM的类路径里只有你自己的Jar,找不到scala-graph库的类,所以就抛出ClassNotFoundException了。
关于你提到的“过去类似项目能直接运行”
大概率是这两种情况之一:
- 过去的项目没有用到任何第三方依赖,或者依赖刚好在你的系统全局类路径里;
- 过去的项目可能无意中配置了能生成包含依赖的“胖Jar”的插件,或者你手动指定了依赖的类路径。
除了sbt-assembly,你还有这些解决方案:
1. 手动指定类路径运行
你可以用scala命令的-cp参数,把你的Jar和依赖Jar都加到类路径里。依赖Jar可以在~/.ivy2/cache/org.scala-graph/graph-core_2.12目录下找到,然后运行:
scala -cp "target/scala-2.12/coloring_2.12-0.1.jar:/path/to/graph-core_2.12-1.12.5.jar" Main
不过这种方式在依赖多的时候会很繁琐。
2. 用SBT生成带依赖的可执行包
你可以用sbt-native-packager插件来生成包含所有依赖的可执行包,修改build.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.9.16") enablePlugins(JavaAppPackaging)
然后执行sbt universal:packageZipTarball,会在target/universal目录下生成一个压缩包,解压后里面有现成的运行脚本,直接就能跑。
3. 直接用SBT的runMain命令
如果你不需要分发Jar,只是本地运行,完全可以跳过打包,直接用:
sbt "runMain Main"
效果和sbt run一样,SBT会自动处理依赖类路径。
内容的提问来源于stack exchange,提问作者Logister

