加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala脚本:在Windows上向ScalaClassLoader解释此类强制转换错误

发布时间:2020-12-16 19:13:51 所属栏目:安全 来源:网络整理
导读:请考虑以下 scala脚本: import scala.reflect.internal.util.ScalaClassLoaderobject Test { def main(args: Array[String]) { val classloaderForScalaLibrary = classOf[ScalaClassLoader.URLClassLoader].getClassLoader println(classloaderForScalaLib
请考虑以下 scala脚本:

import scala.reflect.internal.util.ScalaClassLoader

object Test {
  def main(args: Array[String]) {
    val classloaderForScalaLibrary = classOf[ScalaClassLoader.URLClassLoader].getClassLoader
    println(classloaderForScalaLibrary)
    val classloaderForTestClass = this.getClass.getClassLoader
    println(classloaderForTestClass)
    this.getClass.getClassLoader.asInstanceOf[ScalaClassLoader.URLClassLoader]
  }
}

输出是:

scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@71c8becc
scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@71c8becc
java.lang.ClassCastException: scala.reflect.internal.util.ScalaClassLoader$URLClassLoader cannot be cast to scala.reflect.internal.util.ScalaClassLoader$URLClassLoader
        at Main$.main(Test.scala:8)
        at Main.main(Test.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run$2(ScalaClassLoader.scala:98)
        at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:32)
...

为什么我不能将ScalaClassLoader $URLClassLoader转换为ScalaClassLoader $URLClassLoader?

enter image description here

编辑:

在跑步时:

scala -J-verbose:class Test.scala | grep ScalaClassLoader

输出是:

[Loaded scala.reflect.internal.util.ScalaClassLoader$URLClassLoader from file:/C:/Development/Software/scala-2.12.2/lib/scala-reflect.jar]
...
...
[Loaded scala.reflect.internal.util.ScalaClassLoader$URLClassLoader from file:/C:/DEVELO~1/Software/SCALA-~1.2/lib/scala-reflect.jar]

所以肯定会有一些阴暗的类加载.现在试着调查为什么会这样

解决方法

如果您将代码扩展一点,如下所示:

import scala.reflect.internal.util.ScalaClassLoader

object test {

  def main(args: Array[String]) {
    val cl1 = this.getClass.getClassLoader
    println(cl1)
    val c1 = cl1.getClass 
    println(cl1.getClass)
    println(cl1.getClass.getClassLoader)

    println("-------")

    var c2 = classOf[ScalaClassLoader.URLClassLoader]
    println(c2)
    println(c2.getClassLoader)
    println("-------")
    println(c1 == c2)

  }
}

你会得到以下输出:

scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@5cee5251

class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader

sun.misc.Launcher$AppClassLoader@4554617c

——-

class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader

scala.reflect.internal.util.ScalaClassLoader$URLClassLoader@5cee5251

——-

false

注意匹配哈希@ 5cee5251.
这意味着第一个Scala解释器使用根Java类加载器加载ScalaClassLoader $URLClassLoader,然后使用该类加载器加载脚本中的所有类,当您在代码中请求ScalaClassLoader $URLClassLoader时,它将加载另一个(已加载的)实例ScalaClassLoader $URLClassLoader.通过这种方式,您的脚本将与执行它的“运行时环境”隔离开来.

您可以在ScalaClassLoader.asContext方法中找到一些详细信息,您可以在堆栈跟踪中看到,它使用Thread.setContextClassLoader将自身设置为执行脚本的线程的主类加载器.

更新(为什么它在Mac上运行但在Windows上不起作用)

* nix的scala shell脚本和Windows的scala.bat之间的主要区别在于默认情况下* nix平台标准Scala库被添加到Boot Classpath(请参阅脚本中的usebootcp),而在Windows上它们被添加到“常用类路径” ”.这很重要,因为它定义了哪个类加载器将加载scala.tools.nsc.MainGenericRunner使用的scala.reflect.internal.util.ScalaClassLoader:它是否为根类加载器(如果调用getClassLoader则表示为null)或Application Class Loader(即sun.misc.Launcher $AppClassLoader的实例).这很重要,因为CommonRunner.run仅使用没有父级的URL创建ScalaClassLoader的实例

def run(urls: Seq[URL],objectName: String,arguments: Seq[String]) {
  (ScalaClassLoader fromURLs urls).run(objectName,arguments)
}

这意味着“主”ScalaClassLoader的父类加载器将是引导类加载器而不是sun.misc.Launcher $AppClassLoader,因此当你向scala.reflect.internal.util.ScalaClassLoader类询问这个“主”ScalaClassLoader时它可以在类加载器链加载的类中找不到它,因此必须再次加载它.这就是为什么脚本中有两个不同的ScalaClassLoader类实例的原因.

有两个明显的解决方法(两者都不太好):

>更改Scala源中的CommonRunner.run,实际将当前上下文类加载器作为新ScalaClassLoader的父级传递(可能不那么容易?)
>将scala.bat更改为使用-Xbootclasspath / a:而不是-cp用于%_ TOOL_CLASSPATH%.然而,在* nix脚本中查看usebootcp我可以看到以下注释:

# default to the boot classpath for speed,except on cygwin/mingw/msys because
# JLine on Windows requires a custom DLL to be loaded.
unset usebootcp
if [[ -z "$cygwin$mingw$msys" ]]; then
  usebootcp="true"
fi

所以我怀疑如果你想将scala.bat用于REPL,将所有Scala库移动到Boot Classpath可能是个坏主意.如果是这种情况,您可能需要创建scala.bat的副本(例如scala_run_script.bat)更改它并使用它来运行Scala脚本,为REPL保留标准scala.bat.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读