在SBT中使用自定义类加载器来获取模块依赖项

lar*_*srh 9 scala classloader sbt

我有一个多模块构建SBT组成的api,corethird-party.结构大致如下:

api
|- core
|- third-party
Run Code Online (Sandbox Code Playgroud)

third-party实现的代码api和从其他地方逐字复制,所以我真的不想触摸它.

由于实施的方式third-party(大量使用单身人士),我不能只core依赖third-party.具体来说,我只需要通过它来使用它api,但我需要third-party在运行时拥有多个独立的副本.(这允许我同时拥有多个"单身人士".)

如果我在我的SBT构建之外运行,我只是这样做:

def createInstance(): foo.bar.API = {
  val loader = new java.net.URLClassLoader("path/to/third-party.jar", parent)
  loader.loadClass("foo.bar.Impl").asSubclass(classOf[foo.bar.API]).newInstance()
}
Run Code Online (Sandbox Code Playgroud)

但问题是,我不知道如何在运行时弄清楚URLClassLoader如果我正在运行,我应该给出什么作为参数sbt core/run.

knu*_*ker 5

这应该可行,但我没有用你的设置测试它.

基本思想是让sbt将类路径写入可在运行时使用的文件中.sbt-buildinfo 已经为此提供了一个很好的基础,所以我将在这里使用它,但你可能只提取相关部分而不使用这个插件.

将其添加到项目定义中:

lazy val core = project enablePlugins BuildInfoPlugin settings (
  buildInfoKeys := Seq(BuildInfoKey.map(exportedProducts in (`third-party`, Runtime)) {
    case (_, classFiles) ? ("thirdParty", classFiles.map(_.data.toURI.toURL)) 
  })
  ...
Run Code Online (Sandbox Code Playgroud)

在运行时,使用此:

def createInstance(): foo.bar.API = {
  val loader = new java.net.URLClassLoader(buildinfo.BuildInfo.thirdParty.toArray, parent)
  loader.loadClass("foo.bar.Impl").asSubclass(classOf[foo.bar.API]).newInstance()
}
Run Code Online (Sandbox Code Playgroud)

exportedProducts仅包含项目的已编译类(例如.../target/scala-2.10/classes/).根据您的设置,您可能希望使用fullClasspath(也包含libraryDependencies和依赖项目)或任何其他类路径相关键.