Вы можете использовать вывод задачи fullClasspathAsJars
SBT, чтобы получить доступ к JAR, созданным из вашего исходного кода. Эта задача не включает JAR-файлы зависимостей. Затем вы можете создать ClassLoader
для загрузки классов из этих JAR:
import java.net.URLClassLoader
val classLoader = taskKey[ClassLoader]("Class loader for source classes")
classLoader := {
val jarUrls = (Compile / fullClasspathAsJars).value.map(_.data.toURI.toURL).toArray
new URLClassLoader(jarUrls, ClassLoader.getSystemClassLoader)
}
Затем, если вы знаете имя своего класса в JAR, вы можете использовать этот ClassLoader
для его загрузки.
Обратите внимание на разницу между Scala именами классов и именами классов в JAR. Имена классов Scala могут быть изменены, и один класс Scala может создавать несколько классов в JAR. Например, класс my.company.Box.MyClass
из следующего фрагмента создает два класса JAR: my.company.Box$MyClass
и my.company.Box$MyClass$
, причем последний является классом сопутствующего объекта.
package my.company
object Box {
case class MyClass()
}
Итак, если вы хотите укажите класс по его имени Scala или чтобы перечислить все классы, определенные в источнике, вы должны использовать вывод задачи compile
SBT. Эта задача создает объект CompileAnalysis
, который является частью внутреннего SBT API и может измениться в будущем. Следующий код работает с SBT 1.3.10.
Чтобы загрузить класс по его Scala имени:
import sbt.internal.inc.Analysis
import xsbti.compile.CompileAnalysis
def loadClass(
scalaClassName: String,
classLoader: ClassLoader,
compilation: CompileAnalysis
): List[Class[_]] = {
compilation match {
case analysis: Analysis =>
analysis.relations.productClassName
.forward(scalaClassName)
.map(classLoader.loadClass)
.toList
}
}
classToLoad := "my.company.Box.MyClass"
loadedClass := loadClass(
classToLoad.value,
classLoader.value,
(Compile / compile).value)
Чтобы вывести список всех классов из исходного кода:
def loadAllClasses(
classLoader: ClassLoader,
compilation: CompileAnalysis,
): List[Class[_]] = {
val fullClassNames = compilation match {
case analysis: Analysis =>
analysis.relations.allSources.flatMap { source =>
// Scala class names
val classNames = analysis.relations.classNames(source)
val getProductName = analysis.relations.productClassName
classNames.flatMap { className =>
// Class names in the JAR
val productNames = getProductName.forward(className)
if (productNames.isEmpty) Set(className) else productNames
}
}.toList
}
fullClassNames.map(className => classLoader.loadClass(className))
}
loadedClasses := loadAllClasses(
classLoader.value,
(Compile / compile).value)