使OpenJDK 9+真正起作用的ScalaFX背后的魔力是什么? [英] What's the magic behind ScalaFX to make OpenJDK 9+ actually work?

查看:135
本文介绍了使OpenJDK 9+真正起作用的ScalaFX背后的魔力是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

环境:

  • OpenJDK 64位服务器VM Zulu12.2 + 3-CA(内部版本12.0.1 + 12,混合模式,共享)
  • Scala 2.12.7
  • Windows 10专业版,X86_64
  • IntelliJ IDEA 2019.1.3(最终版)

我从 GitHub 中检出了scalafx-hello-world,并在IntelliJ中构建并运行了它而且一切正常.这里是重要的应用程序的快速实现:

I checked out the scalafx-hello-world from GitHub, built and ran it in IntelliJ and it worked all fine. Here quickly the significant application implementation:

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:

My 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
)

之后,我将实现更改为:

After that, I changed the implementation to:

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不会发生模块问题?

Now my question is: What does ScalaFX that the module problem does not occur?

推荐答案

我无法完全重现您的问题,但是我已经能够获得仅使用 JavaFX 的项目(即,它不使用 ScalaFX )来构建和运行.

I've not been able to exactly reproduce your problem, but I have been able to get a project that uses JavaFX-only (that is, it doesn't make use of ScalaFX) to build and run.

这就是我正在使用的内容(构建文件中指定了所有其他内容):

Here's what I'm using (everything else is specified in the build file):

  • Zulu OpenJDK 11
  • SBT 1.2.8

(我确实尝试使用 Zulu OpenJDK 12来构建和运行该项目,并且也能正常工作.但是,最好使用 OpenJFX 的版本匹配 JDK .)

(I did try using Zulu OpenJDK 12 to build and run the project, and that worked too. However, it's probably best if you use the version of OpenJFX that matches the JDK.)

当我尝试使用原始资源和build.sbt时,从命令行执行sbt run命令时遇到以下错误:

When I tried your original sources and build.sbt, I encountered the following error when executing an sbt run command from the command line:

D:\src\javafx11>sbt run
[info] Loading global plugins from {my home directory}\.sbt\1.0\plugins
[info] Loading project definition from D:\src\javafx11\project
[info] Loading settings for project javafx11 from build.sbt ...
[info] Set current project to JavaFX 11 Hello World (in build file:/D:/src/javafx11/)
[info] Running (fork) hello.ScalaFXHelloWorld
[error] Error: JavaFX runtime components are missing, and are required to run this application
[error] Nonzero exit code returned from runner: 1
[error] (Compile / run) Nonzero exit code returned from runner: 1
[error] Total time: 1 s, completed Aug 11, 2019, 3:17:07 PM

正如我在对您的问题的原始评论中提到的那样.

as I mentioned in my original comments to your question.

我认为这很奇怪,因为代码已编译,这意味着编译器能够很好地找到 JavaFX 运行时.

I thought that was odd because the code compiled, which meant that the compiler was able to find the JavaFX runtime just fine.

然后我通过注释掉构建文件中的fork := true来尝试运行没有 forking 的程序.你猜怎么了?该程序运行没有错误!

I then tried running the program without forking, by commenting out the fork := true in the build file. Guess what? The program ran without error!

关于将 SBT JDK 9+版本一起使用,我可能会遗漏一些东西,但这表明 SBT 在某种程度上没有运行分支正确处理.我可以通过将以下内容添加到构建文件的末尾来强制分叉的进程正常运行:

I may be missing something, regarding using SBT with JDK versions 9+, but this indicated that SBT was somehow not running the forked process correctly. I could force the forked process to run correctly by adding the following to the end of the build file:

val fs = File.separator
val fxRoot = s"${sys.props("user.home")}${fs}.ivy2${fs}cache${fs}org.openjfx${fs}javafx-"
val fxPaths = javaFXModules.map {m =>
  s"$fxRoot$m${fs}jars${fs}javafx-$m-11-$osName.jar"
}
javaOptions ++= Seq(
  "--module-path", fxPaths.mkString(";"),
  "--add-modules", "ALL-MODULE-PATH"
)

这可以通过将下载的 ivy 管理的 JavaFX jar文件添加到 Java 的模块路径中来进行.但是,这对于运行独立应用程序不是一个好的解决方案. sbt-native-packager可能会为完整的应用程序运行提供必要的环境,但我还没有尝试过.

This works by adding the downloaded ivy-managed JavaFX jar files to Java's module path. However, this is not a good solution for running standalone applications. It may be possible for the sbt-native-packager to provide the necessary environment for the completed application to run, but I haven't tried that.

我已经在 GitHub

让我知道这是否有帮助.同时,我将研究 SBT JDK 9+模块的支持,看看是否有更简单的解决方案...

Let me know whether this helps. In the meantime, I'll look into SBT's support for JDK 9+ modules to see whether there is a simpler solution...

更新:

我已经提出了 SBT 的问题(#4941)团队以对此进行更详细的研究.

I have raised an issue (#4941) with the SBT team to look into this in more detail.

更新2

我修复了一个问题,该问题导致该解决方案无法在 Linux 上运行.执行 git pull 来更新源.

I patched an issue that stopped the solution from working on Linux. Perform a git pull to update the sources.

更新3

我还应该提到,最好让 IntelliJ 使用 SBT 运行应用程序,这使事情变得简单,并确保正确配置了应用程序的环境.

I should also mention that it's best to have IntelliJ run the application using SBT, which keeps things simple and ensures that the application's environment is properly configured.

为此,进入 IntelliJ 运行菜单,然后选择 Edit Configurations ... 选项.单击对话框左上角的 + 按钮,从添加新配置"下的列表中选择 sbt任务" ,然后进行如下配置:

To do this, got to the IntelliJ Run menu, and select the Edit Configurations... option. Click the + button at the top left corner of the dialog, select sbt Task" from the list under **Add New Configuration, then configure as follows:

如果需要,它将首先编译并构建应用程序.

This will compile and build the application first, if required.

注意:_VM参数用于运行 SBT ,与 SBT 如何运行分支的应用程序无关.

Note: The _VM parameters are for running SBT, and do not relate to how SBT runs your forked application.

(您也可以添加 SBT 运行配置以测试您的代码.)

(You can also add SBT run configurations to test your code, as well.)

这篇关于使OpenJDK 9+真正起作用的ScalaFX背后的魔力是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆