gpt4 book ai didi

scala - 让 OpenJDK 9+ 真正工作的 ScalaFX 背后有什么魔力?

转载 作者:行者123 更新时间:2023-12-04 15:29:09 25 4
gpt4 key购买 nike

环境 :

  • OpenJDK 64-Bit Server VM Zulu12.2+3-CA(build 12.0.1+12,混合模式,共享)
  • 斯卡拉 2.12.7
  • Windows 10 专业版,X86_64
  • IntelliJ IDEA 2019.1.3(终极版)

  • 我查看了 scalafx-hello-world来自 GitHub ,在 IntelliJ 中构建并运行它,一切正常。这里快速重要的应用程序实现:
    package hello

    import scalafx.application.JFXApp
    import scalafx.application.JFXApp.PrimaryStage
    import scalafx.geometry.Insets
    import scalafx.scene.Scene
    import scalafx.scene.effect.DropShadow
    import scalafx.scene.layout.HBox
    import scalafx.scene.paint.Color._
    import scalafx.scene.paint._
    import scalafx.scene.text.Text

    object ScalaFXHelloWorld extends JFXApp {

    stage = new PrimaryStage {
    // initStyle(StageStyle.Unified)
    title = "ScalaFX Hello World"
    scene = new Scene {
    fill = Color.rgb(38, 38, 38)
    content = new HBox {
    padding = Insets(50, 80, 50, 80)
    children = Seq(
    new Text {
    text = "Scala"
    style = "-fx-font: normal bold 100pt sans-serif"
    fill = new LinearGradient(
    endX = 0,
    stops = Stops(Red, DarkRed))
    },
    new Text {
    text = "FX"
    style = "-fx-font: italic bold 100pt sans-serif"
    fill = new LinearGradient(
    endX = 0,
    stops = Stops(White, DarkGray)
    )
    effect = new DropShadow {
    color = DarkGray
    radius = 15
    spread = 0.25
    }
    }
    )
    }
    }

    }
    }

    编辑:我的 build.sbt:
    // Name of the project
    name := "ScalaFX Hello World"

    // Project version
    version := "11-R16"

    // Version of Scala used by the project
    scalaVersion := "2.12.7"

    // Add dependency on ScalaFX library
    libraryDependencies += "org.scalafx" %% "scalafx" % "11-R16"
    resolvers += Resolver.sonatypeRepo("snapshots")

    scalacOptions ++= Seq("-unchecked", "-deprecation", "-Xcheckinit", "-encoding", "utf8", "-feature")

    // Fork a new JVM for 'run' and 'test:run', to avoid JavaFX double initialization problems
    fork := true

    // Determine OS version of JavaFX binaries
    lazy val osName = System.getProperty("os.name") match {
    case n if n.startsWith("Linux") => "linux"
    case n if n.startsWith("Mac") => "mac"
    case n if n.startsWith("Windows") => "win"
    case _ => throw new Exception("Unknown platform!")
    }

    // Add JavaFX dependencies
    lazy val javaFXModules = Seq("base", "controls", "fxml", "graphics", "media", "swing", "web")
    libraryDependencies ++= javaFXModules.map( m=>
    "org.openjfx" % s"javafx-$m" % "11" classifier osName
    )

    之后,我将实现更改为:
    package hello

    import javafx.application.Application
    import javafx.scene.Scene
    import javafx.scene.control.Label
    import javafx.stage.Stage

    class ScalaFXHelloWorld extends Application {
    override def start(stage: Stage): Unit = {
    stage.setTitle("Does it work?")
    stage.setScene(new Scene(
    new Label("It works!")
    ))
    stage.show()
    }
    }

    object ScalaFXHelloWorld {
    def main(args: Array[String]): Unit = {
    Application.launch(classOf[ScalaFXHelloWorld], args: _*)
    }
    }

    在这里,我收到以下错误:
    Exception in Application start method
    java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
    Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:835)
    Caused by: java.lang.IllegalAccessError: superclass access check failed: class com.sun.javafx.scene.control.ControlHelper (in unnamed module @0x40ac0fa0) cannot access class com.sun.javafx.scene.layout.RegionHelper (in module javafx.graphics) because module javafx.graphics does not export com.sun.javafx.scene.layout to unnamed module @0x40ac0fa0
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    at javafx.scene.control.Control.<clinit>(Control.java:86)
    at hello.ScalaFXHelloWorld.start(ScalaFXHelloWorld.scala:39)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    ... 1 more
    Exception running application hello.ScalaFXHelloWorld

    现在我的问题是:ScalaFX 没有出现什么模块问题?

    最佳答案

    添加到乔纳森克罗斯默的答案:

    以不同方式命名类和对象的原因是,如果主类扩展 javafx.application.Application,Java 启动器实际上具有特殊行为。 .如果您有可用的 Java 源代码,可以在 JAVA_HOME/lib/src.zip/java.base/sun/launcher/LauncherHelper.java 中找到相关代码。 .特别是有两种有趣的方法:

    public static Class<?> checkAndLoadMain(boolean, int ,String)

    //In nested class FXHelper
    private static void setFXLaunchParameters(String, int)

    第一个方法有一个检查主类是否扩展 javafx.application.Application .如果是,此方法将主类替换为嵌套类 FXHelper , 它有自己的 public static void main(String[] args) .

    第二种方法直接由第一种方法调用,它尝试加载 JavaFX 运行时。然而,这样做的方法是首先加载模块 javafx.graphics。通过 java.lang.ModuleLayer.boot().findModule(JAVAFX_GRAPHICS_MODULE_NAME) .
    如果此调用失败,Java 将提示没有找到 JavaFX 运行时,然后立即通过 System.exit(1) 退出.

    回到 SBT 和 Scala,还有一些其他的细节在起作用。首先,如果主对象和扩展类 javafx.application.Application同名,Scala 编译器会生成一个类文件,它都扩展了 Application并且有一个 public static void main(...) .这意味着将触发上述特殊行为,并且 Java 启动器将尝试将 JavaFX 运行时作为模块加载。由于 SBT 目前没有关于模块的概念,JavaFX 运行时将不在模块路径上,并且调用 findModule(...)将失败。

    另一方面,如果主对象与主类有不同的名称,Scala 编译器将放置 public static void main(...)在不扩展 Application 的类中,这反过来意味着 main() 方法将正常执行。

    在我们继续之前,我们应该注意,虽然 SBT 没有将 JavaFX 运行时放在模块路径上,但实际上它确实把它放在了类路径上。这意味着 JavaFX 类对 JVM 是可见的,它们只是不能作为模块加载。毕竟

    A modular JAR file is like an ordinary JAR file in all possible ways, except that it also includes a module-info.class file in its root directory.



    (来自 The State of the Module System)

    但是,如果碰巧调用了一个方法,比如说 Application.launch(...) ,Java 将愉快地加载 javafx.application.Application从类路径。 Application.launch(...)同样可以访问 JavaFX 的其余部分,一切正常。

    这也是为什么在没有 fork 的情况下运行 JavaFX 应用程序的原因。在这种情况下,SBT 将始终调用 public static void main(...)直接,这意味着不会触发来自 java 启动器的特殊行为,并且将在类路径中找到 JavaFX 运行时。

    下面是一个片段,可以看到上述行为的实际效果:

    Main.scala:
    object Main {
    def main(args: Array[String]): Unit = {
    /*
    Try to load the JavaFX runtime as a module. This is what happens if the main class extends
    javafx.application.Application.
    */
    val foundModule = ModuleLayer.boot().findModule("javafx.graphics").isPresent
    println("ModuleLayer.boot().findModule(\"javafx.graphics\").isPresent = " + foundModule) // false

    /*
    Try to load javafx.application.Application directly, bypassing the module system. This is what happens if you
    call Application.launch(...)
    */
    var foundClass = false
    try{
    Class.forName("javafx.application.Application")
    foundClass = true
    }catch {
    case e: ClassNotFoundException => foundClass = false
    }
    println("Class.forName(\"javafx.application.Application\") = " + foundClass) //true
    }
    }

    构建.sbt:
    name := "JavaFXLoadTest"

    version := "0.1"

    scalaVersion := "2.13.2"

    libraryDependencies += "org.openjfx" % "javafx-controls" % "14"

    fork := true

    关于scala - 让 OpenJDK 9+ 真正工作的 ScalaFX 背后有什么魔力?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56774366/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com