斯卡拉类加载器混乱 [英] scala classloaders confusion
问题描述
请考虑以下测试程序(使用scala 2.9.0.1)
对象测试
{
def main(args:Array [String])= {
println(ClassLoader.getSystemClassLoader.getResource( toto))
println(this.getClass.getClassLoader.getResource( toto))
println(classOf [Object] .getClassLoader)
}
}
我编译并使用包含文件 toto的 -cp / tmp运行它,得到以下输出:
空
文件:/ tmp / toto
空
=>系统类加载器不包含类路径
=> Object类没有类加载器!
我错过了一些东西吗?
谢谢,
Arjun
第二个null由 java.lang.Class#getClassLoader()
返回该类的类加载器。一些实现可能使用
null来代表引导类加载器。如果此类是由引导程序
类加载器加载的,则此方法在此类实现中将返回
null。
因此,这就是为什么 classOf [Object] .getClassLoader
返回null的原因,它是由引导类加载器加载的(它位于rt.jar中,更具体地说,它位于jar中在$ JAVA_HOME / lib中。)
第一个null较难解释。似乎Scala保持了系统类加载器的原样,仅在其自己的类加载器(scala / util / ClassLoader.scala中的ScalaClassLoader)中添加了-cp选项。
使用以下命令:
object Test {
def main(args:Array [String])= {
println(ClassLoader.getSystemClassLoader)
println(this.getClass.getClassLoader)
println(classOf [Object] .getClassLoader)
}
}
并运行以下命令:
$ scala -cp / temp测试
我们得到以下输出:
sun.misc.Launcher$AppClassLoader@11b86e7
URLClassLoader(
文件:/ C:/developpement/utils/jdk1.6.0_22/ jre / lib / resources.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/rt.jar
文件:/ C:/developpement/utils/jdk1.6.0 _22 / jre / lib / jsse.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/jce.jar
文件:/ C:/ developpement / utils /jdk1.6.0_22/jre/lib/charsets.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/dnsns.jar
文件:/ C: /developpement/utils/jdk1.6.0_22/jre/lib/ext/localedata.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/sunjce_provider.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/sunmscapi.jar
文件:/ C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/ sunpkcs11.jar
文件:/ C:/DEVELO~1/scala/SCALA-~1.1/bin /../ lib / jline.jar
文件:/ C:/ DEVELO〜1 / scala / SCALA-〜1.1 / bin /../ lib / scala-compiler.jar
文件:/ C:/DEVELO~1/scala/SCALA-~1.1/bin /../ lib / scala-dbc.jar
文件:/ C:/DEVELO~1/scala/SCALA-~1.1/bin /../ lib / scala-library.jar
文件:/ C:/ DEVELO〜1 / scala / SCALA -〜1.1 / bin /../ lib / scala-swing.jar
文件:/ C:/DEVELO~1/scala/SCALA-~1.1/bin /../ lib / scalap.jar
文件:/ C:/ temp /
)
null
因此系统类加载器保持不变,但是Scala类加载器从-cp中获得了添加到其中的项目。 / p>
故事的寓意:如果要从类路径访问资源,请不要在Scala中使用系统类加载器。
编辑:好的,我对此进行了更多研究,并且scala.bat正在执行以下命令行(在纯Windows下,已缩短可读性)
java.exe -Xmx256M -Xms32M -Dscala.home = xxx -cp libsfromscalahome scala.tools.nsc.MainGenericRunner -cp / temp测试
因此,命令行中的-cp选项仅作为选项传递给MainGenericRunner,而不是 java。我相信,通过查看代码,您可以在unix下为scala指定-toolcp选项,以获取Java类路径中包含的内容。诸如此类(完全未经测试):
$ scala -toolcp / temp测试
此选项在scala.bat中不可用。这意味着,如果您在Windows下工作,则必须使用
println(this.getClass.getClassLoader .getResource( toto))
我在 Scala语言问题,但是如果您遇到问题,请提出问题并提交修复程序。我敢肯定他们会很高兴的:-)
编辑:我已将此问题发布为 SI 5062 -toolcp应该在Windows的scala.bat 中可用,并在 github 。
Please consider the following test program (using scala 2.9.0.1)
object test
{
def main(args:Array[String]) = {
println(ClassLoader.getSystemClassLoader.getResource("toto"))
println(this.getClass.getClassLoader.getResource("toto"))
println(classOf[Object].getClassLoader)
}
}
I compile it and run it with "-cp /tmp" containing a file "toto", and I get the following output:
null
file:/tmp/toto
null
=> the system classloader does not contain the classpath
=> the Object class has no classloader!
Am I missing something there or is it a (big) bug in scala?!
Thanks, Arjun
The second null is explained by java.lang.Class#getClassLoader()
Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader.
So, this is why classOf[Object].getClassLoader
returns null, it's loaded by the bootstrap classloader (it is in rt.jar, more specifically, it is in a jar which is in $JAVA_HOME/lib).
The first null is harder to explain. It seems that Scala leaves the system classloader as-is, and only adds the options -cp to it's own classloader (ScalaClassLoader in scala/util/ClassLoader.scala).
Using the following:
object Test {
def main(args:Array[String]) = {
println(ClassLoader.getSystemClassLoader)
println(this.getClass.getClassLoader)
println(classOf[Object].getClassLoader)
}
}
and running it with:
$ scala -cp /temp Test
we get the following output:
sun.misc.Launcher$AppClassLoader@11b86e7
URLClassLoader(
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/resources.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/rt.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/jsse.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/jce.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/charsets.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/dnsns.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/localedata.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/sunjce_provider.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/sunmscapi.jar
file:/C:/developpement/utils/jdk1.6.0_22/jre/lib/ext/sunpkcs11.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/jline.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/scala-compiler.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/scala-dbc.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/scala-library.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/scala-swing.jar
file:/C:/DEVELO~1/scala/SCALA-~1.1/bin/../lib/scalap.jar
file:/C:/temp/
)
null
So the System classloader is left untouched, but the Scala classloader gets the items from -cp added to it.
Moral of the story: don't use the system classloader in Scala if you want to access resources from the classpath.
EDIT: Ok, I've investigated this a bit more, and scala.bat is executing the following command line (under pure Windows, shortened for readability)
java.exe -Xmx256M -Xms32M -Dscala.home="xxx" -cp "libsfromscalahome" scala.tools.nsc.MainGenericRunner -cp /temp Test
So the -cp option from the command line is only being passed as an option to MainGenericRunner, not the java. I believe, from looking at the code, that under unix you can specify the -toolcp option to scala to get something included in the java classpath. Something like (totally untested):
$ scala -toolcp /temp Test
This option isn't available in scala.bat. Which means if you're working under windows, you'll have to get the resources using
println(this.getClass.getClassLoader.getResource("toto"))
I couldn't find an issue in the Scala Lang Issues, but if it's a problem for you, raise an issue and submit a fix. I'm sure they'll be thrilled :-)
EDIT: I have raised this as issue SI 5062 -toolcp should be available on windows, in the scala.bat, and provided a pull request for it on github.
这篇关于斯卡拉类加载器混乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!