lihaoyi test$ tree
.
└── Foo.scala
0 directories, 1 file
lihaoyi test$ cat Foo.scala
object Main{
def main(args: Array[String]): Unit = {
println(getClass.getClassLoader.getResourceAsStream("java/lang/String.class"))
println(getClass.getClassLoader.getClass)
println(Thread.currentThread().getContextClassLoader.getResourceAsStream("java/lang/String.class"))
println(Thread.currentThread().getContextClassLoader.getClass)
}
}
lihaoyi test$ sbt run
[info] Loading global plugins from /Users/lihaoyi/.sbt/0.13/plugins
[info] Set current project to test (in build file:/Users/lihaoyi/Dropbox/Workspace/test/)
[info] Updating {file:/Users/lihaoyi/Dropbox/Workspace/test/}test...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/lihaoyi/Dropbox/Workspace/test/target/scala-2.10/classes...
[info] Running Main
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@18e38ff2
class sbt.classpath.ClasspathUtilities$$anon$1
null
class sbt.classpath.ClasspathFilter
[success] Total time: 2 s, completed 29 May, 2017 4:14:11 PM
lihaoyi test$
在这里,我们可以看到getClass.getClassLoader
和Thread.currentThread.getContextClassLoader
返回不同的值。更重要的是,Thread.currentThread.getContextClassLoader
似乎拒绝加载java/lang/String.class
,而另一个则可以。
值得注意的是,当我使用scalac
/ scala
或java
等外部工具运行jar文件时,两个类加载器都能够将类文件作为资源加载
lihaoyi test$ scalac Foo.scala
lihaoyi test$ scala Main
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@1b28cdfa
class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader
sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@7229724f
class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader
我希望SBT的行为类似:让Main.getClass.getClassLoader
和Thread.currentThread().getContextClassLoader
都能够加载java/lang/String.class
作为资源。是什么给了什么?
答案 0 :(得分:3)
Jason Zaugg(反语)的sbt launcher notes提供了一些提示。
sbt / launcher是一个小型Scala应用程序,可以引导任意Scala程序(通常是SBT)描述配置文件并通过Ivy依赖解析来源。
这将创建一个包含Scala 2.10.6的子类加载器。这个孩子包含SBT本身和xsbti / interface-0.13.11.jar。
在为插件代码,Scala编译器或用户代码创建子类加载器时,SBT需要使用非标准类加载器委派来有选择地隐藏类。
sbt 0.13来源中的一些提示:
def makeLoader(classpath: Seq[File], instance: ScalaInstance, nativeTemp: File): ClassLoader =
filterByClasspath(classpath, makeLoader(classpath, instance.loader, instance, nativeTemp))
def makeLoader(classpath: Seq[File], parent: ClassLoader, instance: ScalaInstance, nativeTemp: File): ClassLoader =
toLoader(classpath, parent, createClasspathResources(classpath, instance), nativeTemp)
基本上,sbt是Java应用程序的厨房接收器,它具有任意Scala版本和您的代码,以及您的测试库以及Oracle / OpenJDK的Java库。要构造一个有意义的类路径而不是一遍又一遍地加载它们,它会创建一个类加载器的层次结构,每个类加载器都按一些标准过滤。 (我认为)
答案 1 :(得分:2)
值得注意的是,解决此问题的一种方法是设置
(fork in run) := true,
(connectInput in run) := true,
(outputStrategy in run) := Some(StdoutOutput),
这似乎解决了这个问题,(Thread.currentThread().getContextClassLoader.getResourceAsStream("java/lang/String.class")
现在有效)但引入了其他无关的问题(分叉的JVM需要花一点时间启动,冷启动并需要时间来预热......)